diff --git a/ApplicationLibCode/Commands/CMakeLists_files.cmake b/ApplicationLibCode/Commands/CMakeLists_files.cmake index 1dc49cca3f..9404013634 100644 --- a/ApplicationLibCode/Commands/CMakeLists_files.cmake +++ b/ApplicationLibCode/Commands/CMakeLists_files.cmake @@ -95,6 +95,7 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RicImportSummaryCalculationExpressionsFeature.h ${CMAKE_CURRENT_LIST_DIR}/RicImportWellLogCsvFileFeature.h ${CMAKE_CURRENT_LIST_DIR}/RicNewViewForGridEnsembleFeature.h + ${CMAKE_CURRENT_LIST_DIR}/RicNewVfpPlotFeature.h ) set(SOURCE_GROUP_SOURCE_FILES @@ -193,6 +194,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RicImportSummaryCalculationExpressionsFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicImportWellLogCsvFileFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicNewViewForGridEnsembleFeature.cpp + ${CMAKE_CURRENT_LIST_DIR}/RicNewVfpPlotFeature.cpp ) if(RESINSIGHT_USE_QT_CHARTS) diff --git a/ApplicationLibCode/Commands/RicDeleteSubItemsFeature.cpp b/ApplicationLibCode/Commands/RicDeleteSubItemsFeature.cpp index d0608ab62c..aa3dca2c0d 100644 --- a/ApplicationLibCode/Commands/RicDeleteSubItemsFeature.cpp +++ b/ApplicationLibCode/Commands/RicDeleteSubItemsFeature.cpp @@ -27,6 +27,7 @@ #include "RimWellPathFractureCollection.h" #include "cafPdmUiItem.h" +#include "cafPdmUiObjectHandle.h" #include "cafSelectionManager.h" #include @@ -115,6 +116,14 @@ bool RicDeleteSubItemsFeature::hasDeletableSubItems( caf::PdmUiItem* uiItem ) } } + { + auto collection = dynamic_cast( uiItem ); + if ( collection && collection->plotCount() > 0 ) + { + return true; + } + } + return false; } @@ -227,5 +236,18 @@ void RicDeleteSubItemsFeature::deleteSubItems( bool onlyDeleteUnchecked ) if ( proj ) proj->reloadCompletionTypeResultsInAllViews(); } } + + { + auto collection = dynamic_cast( item ); + if ( collection ) + { + collection->deleteAllPlots(); + + if ( auto objHandle = dynamic_cast( item ) ) + { + objHandle->updateConnectedEditors(); + } + } + } } } diff --git a/ApplicationLibCode/Commands/RicImportVfpDataFeature.cpp b/ApplicationLibCode/Commands/RicImportVfpDataFeature.cpp index 997de63cc0..06920c73bc 100644 --- a/ApplicationLibCode/Commands/RicImportVfpDataFeature.cpp +++ b/ApplicationLibCode/Commands/RicImportVfpDataFeature.cpp @@ -23,6 +23,7 @@ #include "RimMainPlotCollection.h" +#include "VerticalFlowPerformance/RimVfpDataCollection.h" #include "VerticalFlowPerformance/RimVfpDeck.h" #include "VerticalFlowPerformance/RimVfpPlot.h" #include "VerticalFlowPerformance/RimVfpPlotCollection.h" @@ -38,15 +39,6 @@ CAF_CMD_SOURCE_INIT( RicImportVfpDataFeature, "RicImportVfpDataFeature" ); -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -bool RicImportVfpDataFeature::isCommandEnabled() const -{ - auto plotColl = caf::firstAncestorOfTypeFromSelectedObject(); - return ( plotColl != nullptr ); -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -83,47 +75,17 @@ void RicImportVfpDataFeature::onActionTriggered( bool isChecked ) std::vector vfpPlots; std::vector vfpDecks; + auto vfpDataColl = RimVfpDataCollection::instance(); + for ( const auto& fileName : fileNames ) { - if ( fileName.contains( ".DATA" ) ) - { - auto vfpDeck = vfpPlotColl->addDeck( fileName ); - vfpDecks.push_back( vfpDeck ); - } - else - { - auto vfpPlot = new RimVfpPlot(); - vfpPlot->setFileName( fileName ); - vfpPlotColl->addPlot( vfpPlot ); - - vfpPlots.push_back( vfpPlot ); - } + auto vfpDataSource = vfpDataColl->appendTableDataObject( fileName ); + auto firstPlot = vfpPlotColl->createAndAppendPlots( vfpDataSource ); + vfpDataColl->updateAllRequiredEditors(); + RiuPlotMainWindowTools::onObjectAppended( firstPlot, firstPlot ); } - vfpPlotColl->updateConnectedEditors(); - - for ( auto deck : vfpDecks ) - { - deck->loadDataAndUpdate(); - deck->updateConnectedEditors(); - } - - for ( auto plot : vfpPlots ) - { - plot->loadDataAndUpdate(); - } - - RiuPlotMainWindowTools::showPlotMainWindow(); - - if ( !vfpPlots.empty() ) - { - RiuPlotMainWindowTools::onObjectAppended( vfpPlots.front() ); - } - - if ( !vfpDecks.empty() ) - { - RiuPlotMainWindowTools::onObjectAppended( vfpDecks.front() ); - } + vfpPlotColl->updateAllRequiredEditors(); } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/Commands/RicImportVfpDataFeature.h b/ApplicationLibCode/Commands/RicImportVfpDataFeature.h index 801d8545f0..e7af6a103b 100644 --- a/ApplicationLibCode/Commands/RicImportVfpDataFeature.h +++ b/ApplicationLibCode/Commands/RicImportVfpDataFeature.h @@ -28,7 +28,6 @@ class RicImportVfpDataFeature : public caf::CmdFeature CAF_CMD_HEADER_INIT; private: - bool isCommandEnabled() const override; void onActionTriggered( bool isChecked ) override; void setupActionLook( QAction* actionToSetup ) override; }; diff --git a/ApplicationLibCode/Commands/RicNewVfpPlotFeature.cpp b/ApplicationLibCode/Commands/RicNewVfpPlotFeature.cpp new file mode 100644 index 0000000000..90a21871db --- /dev/null +++ b/ApplicationLibCode/Commands/RicNewVfpPlotFeature.cpp @@ -0,0 +1,59 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2024 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 "RicNewVfpPlotFeature.h" + +#include "RimMainPlotCollection.h" + +#include "VerticalFlowPerformance/RimVfpDataCollection.h" +#include "VerticalFlowPerformance/RimVfpPlotCollection.h" +#include "VerticalFlowPerformance/RimVfpTableData.h" + +#include "cafSelectionManagerTools.h" + +#include "RiuPlotMainWindowTools.h" + +#include + +CAF_CMD_SOURCE_INIT( RicNewVfpPlotFeature, "RicNewVfpPlotFeature" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicNewVfpPlotFeature::onActionTriggered( bool isChecked ) +{ + RimVfpPlotCollection* vfpPlotColl = RimMainPlotCollection::current()->vfpPlotCollection(); + if ( !vfpPlotColl ) return; + + auto selectedTableData = caf::selectedObjectsByTypeStrict(); + for ( auto tableData : selectedTableData ) + { + RimVfpPlot* firstPlot = vfpPlotColl->createAndAppendPlots( tableData ); + vfpPlotColl->updateConnectedEditors(); + RiuPlotMainWindowTools::onObjectAppended( firstPlot, firstPlot ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicNewVfpPlotFeature::setupActionLook( QAction* actionToSetup ) +{ + actionToSetup->setText( "Create VFP Plot" ); + actionToSetup->setIcon( QIcon( ":/VfpPlot.svg" ) ); +} diff --git a/ApplicationLibCode/Commands/RicNewVfpPlotFeature.h b/ApplicationLibCode/Commands/RicNewVfpPlotFeature.h new file mode 100644 index 0000000000..300d859e15 --- /dev/null +++ b/ApplicationLibCode/Commands/RicNewVfpPlotFeature.h @@ -0,0 +1,33 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2024 Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "cafCmdFeature.h" + +//================================================================================================== +/// +//================================================================================================== +class RicNewVfpPlotFeature : public caf::CmdFeature +{ + CAF_CMD_HEADER_INIT; + +private: + void onActionTriggered( bool isChecked ) override; + void setupActionLook( QAction* actionToSetup ) override; +}; diff --git a/ApplicationLibCode/Commands/WellLogCommands/RicDeleteSubPlotFeature.cpp b/ApplicationLibCode/Commands/WellLogCommands/RicDeleteSubPlotFeature.cpp index adcc733e7f..1dfec8002a 100644 --- a/ApplicationLibCode/Commands/WellLogCommands/RicDeleteSubPlotFeature.cpp +++ b/ApplicationLibCode/Commands/WellLogCommands/RicDeleteSubPlotFeature.cpp @@ -44,10 +44,7 @@ bool RicDeleteSubPlotFeature::isCommandEnabled() const { if ( RicWellLogPlotCurveFeatureImpl::parentWellAllocationPlot() ) return false; - std::vector selection; - getSelection( selection ); - - return ( !selection.empty() ); + return isAnyDeletablePlotSelected(); } //-------------------------------------------------------------------------------------------------- @@ -150,3 +147,23 @@ void RicDeleteSubPlotFeature::getSelection( std::vector& selection ) c caf::SelectionManager::instance()->objectsByType( &selection ); } } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RicDeleteSubPlotFeature::isAnyDeletablePlotSelected() const +{ + std::vector selection; + getSelection( selection ); + + for ( RimPlot* plot : selection ) + { + if ( !plot ) continue; + + RimMultiPlot* multiPlot = plot->firstAncestorOrThisOfType(); + RimWellLogPlot* wellLogPlot = plot->firstAncestorOrThisOfType(); + if ( multiPlot || wellLogPlot ) return true; + } + + return false; +} diff --git a/ApplicationLibCode/Commands/WellLogCommands/RicDeleteSubPlotFeature.h b/ApplicationLibCode/Commands/WellLogCommands/RicDeleteSubPlotFeature.h index 1a9ce940cb..6c75d58172 100644 --- a/ApplicationLibCode/Commands/WellLogCommands/RicDeleteSubPlotFeature.h +++ b/ApplicationLibCode/Commands/WellLogCommands/RicDeleteSubPlotFeature.h @@ -39,4 +39,5 @@ protected: private: void getSelection( std::vector& selection ) const; + bool isAnyDeletablePlotSelected() const; }; diff --git a/ApplicationLibCode/ProjectDataModel/RiaOpmParserTools.cpp b/ApplicationLibCode/ProjectDataModel/RiaOpmParserTools.cpp index 36b08bac54..e05f7e8ec0 100644 --- a/ApplicationLibCode/ProjectDataModel/RiaOpmParserTools.cpp +++ b/ApplicationLibCode/ProjectDataModel/RiaOpmParserTools.cpp @@ -85,94 +85,6 @@ Opm::VFPProdTable createProductionTable( const Opm::DeckKeyword& keyword ) return { keyword, gaslift_opt_active, unitSystem }; } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -std::vector extractVfpInjectionTables( const std::string& filename ) -{ - std::vector tables; - - try - { - Opm::Parser parser( false ); - const ::Opm::ParserKeywords::VFPINJ kw1; - const ::Opm::ParserKeywords::VFPIDIMS kw2; - - parser.addParserKeyword( kw1 ); - parser.addParserKeyword( kw2 ); - - auto deck = parser.parseFile( filename ); - - std::string keyword = ::Opm::ParserKeywords::VFPINJ::keywordName; - auto keywordList = deck.getKeywordList( keyword ); - for ( auto kw : keywordList ) - { - auto table = createInjectionTable( *kw ); - tables.push_back( table ); - } - } - catch ( Opm::OpmInputError& e ) - { - QString text = QString( "Error detected when parsing '%1'. Imported data might be missing or incomplete.\n%2" ) - .arg( QString::fromStdString( filename ) ) - .arg( QString::fromStdString( e.what() ) ); - - RiaLogging::warning( text ); - } - catch ( ... ) - { - QString text = - QString( "Error detected when parsing '%1'. Imported data might be missing or incomplete." ).arg( QString::fromStdString( filename ) ); - - RiaLogging::warning( text ); - } - - return tables; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -std::vector extractVfpProductionTables( const std::string& filename ) -{ - std::vector tables; - - try - { - Opm::Parser parser( false ); - const ::Opm::ParserKeywords::VFPPROD kw1; - - parser.addParserKeyword( kw1 ); - - auto deck = parser.parseFile( filename ); - - std::string keyword = ::Opm::ParserKeywords::VFPPROD::keywordName; - auto keywordList = deck.getKeywordList( keyword ); - for ( auto kw : keywordList ) - { - auto table = createProductionTable( *kw ); - tables.push_back( table ); - } - } - catch ( Opm::OpmInputError& e ) - { - QString text = QString( "Error detected when parsing '%1'. Imported data might be missing or incomplete.\n%2" ) - .arg( QString::fromStdString( filename ) ) - .arg( QString::fromStdString( e.what() ) ); - - RiaLogging::warning( text ); - } - catch ( ... ) - { - QString text = - QString( "Error detected when parsing '%1'. Imported data might be missing or incomplete." ).arg( QString::fromStdString( filename ) ); - - RiaLogging::warning( text ); - } - - return tables; -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/RiaOpmParserTools.h b/ApplicationLibCode/ProjectDataModel/RiaOpmParserTools.h index 0841fb579a..c1a75d4b49 100644 --- a/ApplicationLibCode/ProjectDataModel/RiaOpmParserTools.h +++ b/ApplicationLibCode/ProjectDataModel/RiaOpmParserTools.h @@ -30,8 +30,6 @@ //-------------------------------------------------------------------------------------------------- namespace RiaOpmParserTools { -std::vector extractVfpInjectionTables( const std::string& filename ); -std::vector extractVfpProductionTables( const std::string& filename ); std::pair, std::vector> extractVfpTablesFromDataFile( const std::string& dataDeckFilename ); diff --git a/ApplicationLibCode/ProjectDataModel/RimOilField.cpp b/ApplicationLibCode/ProjectDataModel/RimOilField.cpp index 0497cf065f..58355cdd4c 100644 --- a/ApplicationLibCode/ProjectDataModel/RimOilField.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimOilField.cpp @@ -38,6 +38,7 @@ #include "RimWellPathCollection.h" #include "Polygons/RimPolygonCollection.h" +#include "VerticalFlowPerformance/RimVfpDataCollection.h" CAF_PDM_SOURCE_INIT( RimOilField, "ResInsightOilField" ); //-------------------------------------------------------------------------------------------------- @@ -93,6 +94,9 @@ RimOilField::RimOilField() ensembleWellLogsCollection = new RimEnsembleWellLogsCollection(); polygonCollection = new RimPolygonCollection(); + CAF_PDM_InitFieldNoDefault( &vfpDataCollection, "VfpDataCollection", "VFP Data" ); + vfpDataCollection = new RimVfpDataCollection(); + m_fractureTemplateCollection_OBSOLETE = new RimFractureTemplateCollection; m_fractureTemplateCollection_OBSOLETE.xmlCapability()->setIOWritable( false ); } diff --git a/ApplicationLibCode/ProjectDataModel/RimOilField.h b/ApplicationLibCode/ProjectDataModel/RimOilField.h index 246ad5ea0f..2974ea42c7 100644 --- a/ApplicationLibCode/ProjectDataModel/RimOilField.h +++ b/ApplicationLibCode/ProjectDataModel/RimOilField.h @@ -44,6 +44,7 @@ class RimEnsembleWellLogsCollection; class RimPolygonCollection; class RimEclipseViewCollection; class RimEclipseContourMapViewCollection; +class RimVfpDataCollection; //================================================================================================== /// @@ -79,6 +80,7 @@ public: caf::PdmChildField ensembleWellLogsCollection; caf::PdmChildField polygonCollection; caf::PdmChildField eclipseContourMapCollection; + caf::PdmChildField vfpDataCollection; protected: void initAfterRead() override; diff --git a/ApplicationLibCode/ProjectDataModel/RimProject.cpp b/ApplicationLibCode/ProjectDataModel/RimProject.cpp index 99ff1d34f5..c6dd1c33a6 100644 --- a/ApplicationLibCode/ProjectDataModel/RimProject.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimProject.cpp @@ -99,6 +99,8 @@ #include "RimWellLogPlotCollection.h" #include "RimWellPath.h" #include "RimWellPathCollection.h" + +#include "VerticalFlowPerformance/RimVfpDataCollection.h" #include "VerticalFlowPerformance/RimVfpPlotCollection.h" #include "Tools/RiaVariableMapper.h" @@ -1458,6 +1460,10 @@ void RimProject::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, Q { uiTreeOrdering.add( oilField->ensembleWellLogsCollection() ); } + if ( oilField->vfpDataCollection() ) + { + uiTreeOrdering.add( oilField->vfpDataCollection() ); + } } } else if ( uiConfigName == "PlotWindow.Scripts" || uiConfigName == "MainWindow.Scripts" ) diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp index 25cd2539b5..7a63fbeefb 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp @@ -1396,17 +1396,10 @@ void RimSummaryCurve::updateTimeAnnotations() void RimSummaryCurve::updateLegendEntryVisibilityNoPlotUpdate() { if ( !m_plotCurve ) return; - - auto ensembleCurveSet = firstAncestorOrThisOfType(); - if ( ensembleCurveSet ) - { - return; - } + if ( !firstAncestorOrThisOfType() ) return; bool showLegendInPlot = m_showLegend(); - - auto summaryPlot = firstAncestorOrThisOfType(); - if ( summaryPlot ) + if ( auto summaryPlot = firstAncestorOrThisOfType() ) { bool anyCalculated = false; for ( const auto c : summaryPlot->summaryCurves() ) diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/CMakeLists_files.cmake b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/CMakeLists_files.cmake index b2d2b68a31..b1fbee6cef 100644 --- a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/CMakeLists_files.cmake +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/CMakeLists_files.cmake @@ -3,6 +3,8 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RimVfpDeck.h ${CMAKE_CURRENT_LIST_DIR}/RimVfpPlot.h ${CMAKE_CURRENT_LIST_DIR}/RimVfpPlotCollection.h + ${CMAKE_CURRENT_LIST_DIR}/RimVfpTableData.h + ${CMAKE_CURRENT_LIST_DIR}/RimVfpDataCollection.h ) set(SOURCE_GROUP_SOURCE_FILES @@ -10,6 +12,8 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RimVfpDefines.cpp ${CMAKE_CURRENT_LIST_DIR}/RimVfpPlot.cpp ${CMAKE_CURRENT_LIST_DIR}/RimVfpPlotCollection.cpp + ${CMAKE_CURRENT_LIST_DIR}/RimVfpTableData.cpp + ${CMAKE_CURRENT_LIST_DIR}/RimVfpDataCollection.cpp ) list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES}) diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDataCollection.cpp b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDataCollection.cpp new file mode 100644 index 0000000000..44fda4eb84 --- /dev/null +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDataCollection.cpp @@ -0,0 +1,73 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2024 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 "RimVfpDataCollection.h" + +#include "RimOilField.h" +#include "RimProject.h" +#include "RimVfpPlotCollection.h" +#include "RimVfpTableData.h" + +CAF_PDM_SOURCE_INIT( RimVfpDataCollection, "RimVfpDataCollection" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimVfpDataCollection::RimVfpDataCollection() +{ + CAF_PDM_InitObject( "VFP Data", ":/VfpPlotCollection.svg" ); + + CAF_PDM_InitFieldNoDefault( &m_vfpTableData, "VfpPlots", "Vertical Flow Performance Data" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimVfpDataCollection* RimVfpDataCollection::instance() +{ + return RimProject::current()->activeOilField()->vfpDataCollection(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimVfpTableData* RimVfpDataCollection::appendTableDataObject( const QString& fileName ) +{ + auto* vfpTableData = new RimVfpTableData(); + vfpTableData->setFileName( fileName ); + + m_vfpTableData.push_back( vfpTableData ); + + return vfpTableData; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimVfpDataCollection::vfpTableData() const +{ + return m_vfpTableData.childrenByType(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimVfpDataCollection::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const +{ + RimVfpPlotCollection::addImportItems( menuBuilder ); +} diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDataCollection.h b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDataCollection.h new file mode 100644 index 0000000000..a5afebf70c --- /dev/null +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDataCollection.h @@ -0,0 +1,46 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2024 Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// +#pragma once + +#include "cafPdmChildArrayField.h" +#include "cafPdmObject.h" + +class RimVfpTableData; + +//================================================================================================== +/// +/// +//================================================================================================== +class RimVfpDataCollection : public caf::PdmObject +{ + CAF_PDM_HEADER_INIT; + +public: + RimVfpDataCollection(); + + static RimVfpDataCollection* instance(); + + RimVfpTableData* appendTableDataObject( const QString& fileName ); + std::vector vfpTableData() const; + +private: + void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override; + +private: + caf::PdmChildArrayField m_vfpTableData; +}; diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDeck.cpp b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDeck.cpp index e8a0780892..3e9ce414d4 100644 --- a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDeck.cpp +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDeck.cpp @@ -18,14 +18,14 @@ #include "RimVfpDeck.h" -#include "RiaOpmParserTools.h" +#include "RigVfpTables.h" +#include "RimVfpDataCollection.h" #include "RimVfpPlotCollection.h" +#include "RimVfpTableData.h" #include "cafPdmUiTreeOrdering.h" -#include - CAF_PDM_SOURCE_INIT( RimVfpDeck, "RimVfpDeck" ); //-------------------------------------------------------------------------------------------------- @@ -35,7 +35,7 @@ RimVfpDeck::RimVfpDeck() { CAF_PDM_InitObject( "VFP Plot", ":/VfpPlot.svg" ); - CAF_PDM_InitFieldNoDefault( &m_filePath, "FilePath", "File Path" ); + CAF_PDM_InitFieldNoDefault( &m_vfpTableData, "VfpTableData", "VFP Data Source" ); CAF_PDM_InitFieldNoDefault( &m_vfpPlotCollection, "VfpPlotCollection", "Plot Collection" ); m_vfpPlotCollection = new RimVfpPlotCollection(); @@ -45,9 +45,9 @@ RimVfpDeck::RimVfpDeck() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimVfpDeck::setFileName( const QString& filename ) +void RimVfpDeck::setDataSource( RimVfpTableData* tableData ) { - m_filePath = filename; + m_vfpTableData = tableData; } //-------------------------------------------------------------------------------------------------- @@ -57,50 +57,40 @@ void RimVfpDeck::loadDataAndUpdate() { updateObjectName(); - auto createRimVfpPlot = [&]() -> RimVfpPlot* - { - auto plot = new RimVfpPlot(); - plot->setFileName( m_filePath().path() ); - return plot; - }; - std::vector currentPlots = m_vfpPlotCollection->plots(); - auto [vfpProdTables, vfpInjTables] = RiaOpmParserTools::extractVfpTablesFromDataFile( m_filePath().path().toStdString() ); - for ( const auto& prodTable : vfpProdTables ) + if ( m_vfpTableData ) { - RimVfpPlot* plot = m_vfpPlotCollection->plotForTableNumber( prodTable.getTableNum() ); - if ( !plot ) - { - plot = createRimVfpPlot(); - m_vfpPlotCollection->addPlot( plot ); - } - else - { - std::erase( currentPlots, plot ); - } - plot->setProductionTable( prodTable ); - plot->setDataIsImportedExternally( true ); - plot->setDeletable( false ); - plot->loadDataAndUpdate(); - } + m_vfpTableData->ensureDataIsImported(); - for ( const auto& injTable : vfpInjTables ) - { - RimVfpPlot* plot = m_vfpPlotCollection->plotForTableNumber( injTable.getTableNum() ); - if ( !plot ) + if ( m_vfpTableData->vfpTables() ) { - plot = createRimVfpPlot(); - m_vfpPlotCollection->addPlot( plot ); + auto tables = m_vfpTableData->vfpTables(); + + auto allTableNumbers = tables->productionTableNumbers(); + auto injTableNumbers = tables->injectionTableNumbers(); + allTableNumbers.insert( allTableNumbers.end(), injTableNumbers.begin(), injTableNumbers.end() ); + + for ( const auto& number : allTableNumbers ) + { + RimVfpPlot* plot = m_vfpPlotCollection->plotForTableNumber( number ); + if ( !plot ) + { + plot = new RimVfpPlot(); + plot->setDataSource( m_vfpTableData ); + plot->setTableNumber( number ); + plot->initializeObject(); + + m_vfpPlotCollection->addPlot( plot ); + } + else + { + std::erase( currentPlots, plot ); + } + plot->setDeletable( false ); + plot->loadDataAndUpdate(); + } } - else - { - std::erase( currentPlots, plot ); - } - plot->setInjectionTable( injTable ); - plot->setDataIsImportedExternally( true ); - plot->setDeletable( false ); - plot->loadDataAndUpdate(); } for ( auto plotToDelete : currentPlots ) @@ -109,6 +99,15 @@ void RimVfpDeck::loadDataAndUpdate() delete plotToDelete; } } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimVfpDeck::plots() const +{ + return m_vfpPlotCollection->plots(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -127,13 +126,30 @@ void RimVfpDeck::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, Q //-------------------------------------------------------------------------------------------------- void RimVfpDeck::updateObjectName() { - QString name = "VFP Plots"; + QString name = "VFP Deck"; - QFileInfo fileInfo( m_filePath().path() ); - auto fileName = fileInfo.fileName(); - if ( !fileName.isEmpty() ) + if ( m_vfpTableData ) { - name += " - " + fileName; + name = m_vfpTableData->name(); } + setName( name ); } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QList RimVfpDeck::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) +{ + QList options; + if ( fieldNeedingOptions == &m_vfpTableData ) + { + RimVfpDataCollection* vfpDataCollection = RimVfpDataCollection::instance(); + for ( auto table : vfpDataCollection->vfpTableData() ) + { + options.push_back( caf::PdmOptionItemInfo( table->name(), table ) ); + } + } + + return options; +} diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDeck.h b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDeck.h index fdc25c5293..d927f2c73a 100644 --- a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDeck.h +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDeck.h @@ -21,8 +21,11 @@ #include "RimNamedObject.h" #include "cafFilePath.h" +#include "cafPdmPtrField.h" class RimVfpPlotCollection; +class RimVfpTableData; +class RimVfpPlot; //-------------------------------------------------------------------------------------------------- /// RimVfpDeck parses a deck file (*.DATA) containing VFP data and creates a collection of VFP plots. @@ -34,14 +37,17 @@ class RimVfpDeck : public RimNamedObject public: RimVfpDeck(); - void setFileName( const QString& filename ); + void setDataSource( RimVfpTableData* tableData ); void loadDataAndUpdate(); -private: - void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override; - void updateObjectName(); + std::vector plots() const; private: - caf::PdmField m_filePath; + void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override; + void updateObjectName(); + QList calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override; + +private: + caf::PdmPtrField m_vfpTableData; caf::PdmChildField m_vfpPlotCollection; }; diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlot.cpp b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlot.cpp index 9bf1cce8d8..d4868e0d63 100644 --- a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlot.cpp +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlot.cpp @@ -19,22 +19,27 @@ #include "RimVfpPlot.h" #include "RiaColorTables.h" +#include "RiaColorTools.h" #include "RiaEclipseUnitTools.h" #include "RiaOpmParserTools.h" +#include "RigVfpTables.h" + #include "RimPlotAxisProperties.h" +#include "RimPlotCurve.h" +#include "RimProject.h" +#include "RimVfpDataCollection.h" #include "RimVfpDefines.h" +#include "RimVfpTableData.h" #include "Tools/RimPlotAxisTools.h" #include "RiuContextMenuLauncher.h" #include "RiuPlotCurve.h" #include "RiuPlotWidget.h" +#include "RiuQwtCurvePointTracker.h" #include "RiuQwtPlotCurveDefines.h" #include "RiuQwtPlotWheelZoomer.h" #include "RiuQwtPlotWidget.h" - -#include "RiuQwtCurvePointTracker.h" -#include "RiuQwtPlotWidget.h" #include "RiuQwtPlotZoomer.h" #include "cafPdmUiComboBoxEditor.h" @@ -51,39 +56,6 @@ // //================================================================================================== -class VfpPlotData -{ -public: - void setXAxisTitle( const QString& xAxisTitle ) { m_xAxisTitle = xAxisTitle; } - void setYAxisTitle( const QString& yAxisTitle ) { m_yAxisTitle = yAxisTitle; } - - const QString& xAxisTitle() const { return m_xAxisTitle; } - const QString& yAxisTitle() const { return m_yAxisTitle; } - - void appendCurve( const QString& curveTitle, const std::vector& xData, const std::vector& yData ) - { - m_curveTitles.push_back( curveTitle ); - m_xData.push_back( xData ); - m_yData.push_back( yData ); - } - - const QString& curveTitle( size_t idx ) const { return m_curveTitles[idx]; } - - size_t size() const { return m_xData.size(); } - - size_t curveSize( size_t idx ) const { return m_xData[idx].size(); } - - const std::vector& xData( size_t idx ) const { return m_xData[idx]; } - const std::vector& yData( size_t idx ) const { return m_yData[idx]; } - -private: - QString m_xAxisTitle; - QString m_yAxisTitle; - std::vector m_curveTitles; - std::vector> m_xData; - std::vector> m_yData; -}; - CAF_PDM_SOURCE_INIT( RimVfpPlot, "VfpPlot" ); //-------------------------------------------------------------------------------------------------- @@ -97,7 +69,10 @@ RimVfpPlot::RimVfpPlot() CAF_PDM_InitField( &m_plotTitle, "PlotTitle", QString( "VFP Plot" ), "Plot Title" ); m_plotTitle.uiCapability()->setUiHidden( true ); - CAF_PDM_InitFieldNoDefault( &m_filePath, "FilePath", "File Path" ); + CAF_PDM_InitFieldNoDefault( &m_filePath_OBSOLETE, "FilePath", "File Path" ); + m_filePath_OBSOLETE.xmlCapability()->setIOWritable( false ); + + CAF_PDM_InitFieldNoDefault( &m_vfpTableData, "VfpTableData", "VFP Data Source" ); caf::AppEnum defaultTableType = RimVfpDefines::TableType::INJECTION; CAF_PDM_InitField( &m_tableType, "TableType", defaultTableType, "Table Type" ); @@ -156,6 +131,8 @@ RimVfpPlot::RimVfpPlot() connectAxisSignals( m_xAxisProperties() ); connectAxisSignals( m_yAxisProperties() ); + CAF_PDM_InitFieldNoDefault( &m_plotCurves, "PlotCurves", "Curves" ); + m_showWindow = true; m_showPlotLegends = true; m_dataIsImportedExternally = false; @@ -163,6 +140,8 @@ RimVfpPlot::RimVfpPlot() setAsPlotMdiWindow(); setDeletable( true ); + + m_vfpTables = std::make_unique(); } //-------------------------------------------------------------------------------------------------- @@ -177,9 +156,43 @@ RimVfpPlot::~RimVfpPlot() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimVfpPlot::setFileName( const QString& filename ) +void RimVfpPlot::setDataSource( RimVfpTableData* vfpTableData ) { - m_filePath = filename; + m_vfpTableData = vfpTableData; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimVfpPlot::setTableNumber( int tableNumber ) +{ + m_tableNumber = tableNumber; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimVfpPlot::initializeObject() +{ + if ( !vfpTables() ) return; + + auto tableNumber = m_tableNumber(); + + // Always use the available table number if only one table is available + auto prodTableNumbers = vfpTables()->productionTableNumbers(); + auto injTableNumbers = vfpTables()->injectionTableNumbers(); + + if ( prodTableNumbers.size() == 1 && injTableNumbers.empty() ) + { + tableNumber = prodTableNumbers.front(); + } + else if ( injTableNumbers.size() == 1 && prodTableNumbers.empty() ) + { + tableNumber = injTableNumbers.front(); + } + + auto table = vfpTables()->getTableInitialData( tableNumber ); + initializeFromInitData( table ); } //-------------------------------------------------------------------------------------------------- @@ -260,72 +273,35 @@ void RimVfpPlot::updateLegend() //-------------------------------------------------------------------------------------------------- QString RimVfpPlot::asciiDataForPlotExport() const { - QString filePath = m_filePath.v().path(); - if ( !filePath.isEmpty() ) + if ( !vfpTables() ) return {}; + + auto tableText = vfpTables()->asciiDataForTable( m_tableNumber(), + m_primaryVariable(), + m_familyVariable(), + m_interpolatedVariable(), + m_flowingPhase(), + tableSelection() ); + + QString wellName; + + if ( m_vfpTableData ) { - QFileInfo fi( filePath ); - QString wellName = fi.baseName(); - - VfpPlotData plotData; - if ( m_tableType() == RimVfpDefines::TableType::PRODUCTION ) + wellName = m_vfpTableData->name(); + } + else + { + QString filePath = m_filePath_OBSOLETE.v().path(); + if ( !filePath.isEmpty() ) { - if ( m_prodTable ) - { - populatePlotData( *m_prodTable, m_primaryVariable(), m_familyVariable(), m_interpolatedVariable(), m_flowingPhase(), plotData ); - } + QFileInfo fi( filePath ); + QString wellName = fi.baseName(); } - else - { - if ( m_injectionTable ) - { - populatePlotData( *m_injectionTable, m_interpolatedVariable(), m_flowingPhase(), plotData ); - } - } - - QString plotTitle = - generatePlotTitle( wellName, m_tableNumber(), m_tableType(), m_interpolatedVariable(), m_primaryVariable(), m_familyVariable() ); - - QString dataText; - - if ( plotData.size() > 0 ) - { - // The curves should have same dimensions - const size_t curveSize = plotData.curveSize( 0 ); - - // Generate the headers for the columns - // First column is the primary variable - QString columnTitleLine( plotData.xAxisTitle() ); - - // Then one column per "family" - for ( size_t s = 0; s < plotData.size(); s++ ) - { - columnTitleLine.append( QString( "\t%1" ).arg( plotData.curveTitle( s ) ) ); - } - columnTitleLine.append( "\n" ); - - dataText.append( columnTitleLine ); - - // Add the rows: one row per primary variable value - for ( size_t idx = 0; idx < curveSize; idx++ ) - { - QString line; - - // First item on each line is the primary variable - line.append( QString( "%1" ).arg( plotData.xData( 0 )[idx] ) ); - - for ( size_t s = 0; s < plotData.size(); s++ ) - { - line.append( QString( "\t%1" ).arg( plotData.yData( s )[idx] ) ); - } - dataText.append( line ); - dataText.append( "\n" ); - } - } - - return QString( "%1\n\n%2" ).arg( plotTitle ).arg( dataText ); } - return {}; + QString plotTitle = + generatePlotTitle( wellName, m_tableNumber(), m_tableType(), m_interpolatedVariable(), m_primaryVariable(), m_familyVariable() ); + + return QString( "%1\n\n%2" ).arg( plotTitle ).arg( tableText ); } //-------------------------------------------------------------------------------------------------- @@ -333,6 +309,13 @@ QString RimVfpPlot::asciiDataForPlotExport() const //-------------------------------------------------------------------------------------------------- void RimVfpPlot::reattachAllCurves() { + for ( auto curve : m_plotCurves() ) + { + if ( curve->isChecked() ) + { + curve->setParentPlotNoReplot( m_plotWidget ); + } + } } //-------------------------------------------------------------------------------------------------- @@ -340,6 +323,10 @@ void RimVfpPlot::reattachAllCurves() //-------------------------------------------------------------------------------------------------- void RimVfpPlot::detachAllCurves() { + for ( auto curve : m_plotCurves() ) + { + curve->detach(); + } } //-------------------------------------------------------------------------------------------------- @@ -385,24 +372,6 @@ void RimVfpPlot::zoomAll() updatePlotWidgetFromAxisRanges(); } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimVfpPlot::setProductionTable( const Opm::VFPProdTable& table ) -{ - m_prodTable = std::make_unique( table ); - m_injectionTable.reset(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimVfpPlot::setInjectionTable( const Opm::VFPInjTable& table ) -{ - m_prodTable.reset(); - m_injectionTable = std::make_unique( table ); -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -422,8 +391,12 @@ int RimVfpPlot::tableNumber() const //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimVfpPlot::doRemoveFromCollection() +void RimVfpPlot::onChildrenUpdated( caf::PdmChildArrayFieldHandle* childArray, std::vector& updatedObjects ) { + detachAllCurves(); + reattachAllCurves(); + + m_plotWidget->scheduleReplot(); } //-------------------------------------------------------------------------------------------------- @@ -500,57 +473,22 @@ void RimVfpPlot::onLoadDataAndUpdate() return; } - m_plotWidget->detachItems( RiuPlotWidget::PlotItemType::CURVE ); - updateLegend(); QString wellName; - if ( !m_dataIsImportedExternally ) + if ( vfpTables() ) { - QString filePath = m_filePath.v().path(); - if ( !filePath.isEmpty() ) - { - QFileInfo fi( filePath ); - wellName = fi.baseName(); + wellName = m_vfpTableData->baseFileName(); - // Try to read the file as an prod table first (most common) - const std::vector tables = RiaOpmParserTools::extractVfpProductionTables( filePath.toStdString() ); - if ( !tables.empty() ) - { - setProductionTable( tables[0] ); - } - else - { - const std::vector tables = RiaOpmParserTools::extractVfpInjectionTables( filePath.toStdString() ); - if ( !tables.empty() ) - { - setInjectionTable( tables[0] ); - } - } - } - } + auto vfpPlotData = vfpTables()->populatePlotData( m_tableNumber(), + m_primaryVariable(), + m_familyVariable(), + m_interpolatedVariable(), + m_flowingPhase(), + tableSelection() ); - if ( m_prodTable ) - { - auto table = *m_prodTable; - m_tableType = RimVfpDefines::TableType::PRODUCTION; - m_tableNumber = table.getTableNum(); - m_referenceDepth = table.getDatumDepth(); - m_flowingPhase = getFlowingPhaseType( table ); - m_flowingGasFraction = getFlowingGasFractionType( table ); - m_flowingWaterFraction = getFlowingWaterFractionType( table ); - populatePlotWidgetWithCurveData( m_plotWidget, table, m_primaryVariable(), m_familyVariable() ); - } - else if ( m_injectionTable ) - { - auto table = *m_injectionTable; - - m_tableType = RimVfpDefines::TableType::INJECTION; - m_tableNumber = table.getTableNum(); - m_referenceDepth = table.getDatumDepth(); - m_flowingPhase = getFlowingPhaseType( table ); - populatePlotWidgetWithCurveData( m_plotWidget, table ); + populatePlotWidgetWithPlotData( m_plotWidget, vfpPlotData ); } updatePlotTitle( @@ -559,85 +497,18 @@ void RimVfpPlot::onLoadDataAndUpdate() m_plotWidget->setAxisTitleEnabled( RiuPlotAxis::defaultBottom(), true ); m_plotWidget->setAxisTitleEnabled( RiuPlotAxis::defaultLeft(), true ); + reattachAllCurves(); + updatePlotWidgetFromAxisRanges(); m_plotWidget->scheduleReplot(); } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimVfpPlot::populatePlotWidgetWithCurveData( RiuPlotWidget* plotWidget, const Opm::VFPInjTable& table ) -{ - VfpPlotData plotData; - populatePlotData( table, m_interpolatedVariable(), m_flowingPhase(), plotData ); - populatePlotWidgetWithPlotData( plotWidget, plotData ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimVfpPlot::populatePlotData( const Opm::VFPInjTable& table, - RimVfpDefines::InterpolatedVariableType interpolatedVariable, - RimVfpDefines::FlowingPhaseType flowingPhase, - VfpPlotData& plotData ) -{ - QString xAxisTitle = axisTitle( RimVfpDefines::ProductionVariableType::FLOW_RATE, flowingPhase ); - plotData.setXAxisTitle( xAxisTitle ); - - QString yAxisTitle = QString( "%1 %2" ).arg( caf::AppEnum::uiText( interpolatedVariable ), - getDisplayUnitWithBracket( RimVfpDefines::ProductionVariableType::THP ) ); - plotData.setYAxisTitle( yAxisTitle ); - - std::vector thpValues = table.getTHPAxis(); - - for ( size_t thp = 0; thp < thpValues.size(); thp++ ) - { - size_t numValues = table.getFloAxis().size(); - std::vector xVals = table.getFloAxis(); - std::vector yVals( numValues, 0.0 ); - for ( size_t y = 0; y < numValues; y++ ) - { - yVals[y] = table( thp, y ); - if ( interpolatedVariable == RimVfpDefines::InterpolatedVariableType::BHP_THP_DIFF ) - { - yVals[y] -= thpValues[thp]; - } - } - - double value = convertToDisplayUnit( thpValues[thp], RimVfpDefines::ProductionVariableType::THP ); - QString unit = getDisplayUnit( RimVfpDefines::ProductionVariableType::THP ); - QString title = QString( "%1 [%2]: %3" ) - .arg( caf::AppEnum::uiText( RimVfpDefines::ProductionVariableType::THP ) ) - .arg( unit ) - .arg( value ); - - convertToDisplayUnit( yVals, RimVfpDefines::ProductionVariableType::THP ); - convertToDisplayUnit( xVals, RimVfpDefines::ProductionVariableType::FLOW_RATE ); - - plotData.appendCurve( title, xVals, yVals ); - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimVfpPlot::populatePlotWidgetWithCurveData( RiuPlotWidget* plotWidget, - const Opm::VFPProdTable& table, - RimVfpDefines::ProductionVariableType primaryVariable, - RimVfpDefines::ProductionVariableType familyVariable ) -{ - VfpPlotData plotData; - populatePlotData( table, primaryVariable, familyVariable, m_interpolatedVariable(), m_flowingPhase(), plotData ); - populatePlotWidgetWithPlotData( plotWidget, plotData ); -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::populatePlotWidgetWithPlotData( RiuPlotWidget* plotWidget, const VfpPlotData& plotData ) { - plotWidget->detachItems( RiuPlotWidget::PlotItemType::CURVE ); plotWidget->setAxisScale( RiuPlotAxis::defaultBottom(), 0, 1 ); plotWidget->setAxisScale( RiuPlotAxis::defaultLeft(), 0, 1 ); plotWidget->setAxisAutoScale( RiuPlotAxis::defaultBottom(), true ); @@ -645,25 +516,45 @@ void RimVfpPlot::populatePlotWidgetWithPlotData( RiuPlotWidget* plotWidget, cons plotWidget->setAxisTitleText( RiuPlotAxis::defaultBottom(), plotData.xAxisTitle() ); plotWidget->setAxisTitleText( RiuPlotAxis::defaultLeft(), plotData.yAxisTitle() ); + if ( m_plotCurves.size() != plotData.size() ) + { + detachAllCurves(); + m_plotCurves.deleteChildren(); + + for ( auto idx = 0u; idx < plotData.size(); idx++ ) + { + QColor qtClr = RiaColorTables::summaryCurveDefaultPaletteColors().cycledQColor( idx ); + + auto curve = new RimPlotCurve(); + + curve->setLineStyle( RiuQwtPlotCurveDefines::LineStyleEnum::STYLE_SOLID ); + curve->setLineThickness( 2 ); + curve->setColor( RiaColorTools::fromQColorTo3f( qtClr ) ); + curve->setSymbol( RiuPlotCurveSymbol::SYMBOL_ELLIPSE ); + curve->setSymbolSize( 6 ); + + m_plotCurves.push_back( curve ); + } + + updateConnectedEditors(); + } + + auto plotCurves = m_plotCurves.childrenByType(); + for ( auto idx = 0u; idx < plotData.size(); idx++ ) { - QColor qtClr = RiaColorTables::summaryCurveDefaultPaletteColors().cycledQColor( idx ); - RiuPlotCurve* curve = m_plotWidget->createPlotCurve( nullptr, plotData.curveTitle( idx ) ); + auto curve = plotCurves[idx]; + if ( !curve ) continue; - curve->setAppearance( RiuQwtPlotCurveDefines::LineStyleEnum::STYLE_SOLID, - RiuQwtPlotCurveDefines::CurveInterpolationEnum::INTERPOLATION_POINT_TO_POINT, - 2, - qtClr ); - - RiuPlotCurveSymbol* symbol = curve->createSymbol( RiuPlotCurveSymbol::PointSymbolEnum::SYMBOL_ELLIPSE ); - symbol->setColor( qtClr ); - symbol->setSize( 6, 6 ); - curve->setSymbol( symbol ); - - bool useLogarithmicScale = false; - curve->setSamplesFromXValuesAndYValues( plotData.xData( idx ), plotData.yData( idx ), useLogarithmicScale ); - curve->attachToPlot( plotWidget ); - curve->showInPlot(); + curve->setCustomName( plotData.curveTitle( idx ) ); + curve->setParentPlotNoReplot( plotWidget ); + if ( curve->plotCurve() ) + { + bool useLogarithmicScale = false; + curve->plotCurve()->setSamplesFromXValuesAndYValues( plotData.xData( idx ), plotData.yData( idx ), useLogarithmicScale ); + } + curve->updateCurveAppearance(); + curve->appearanceChanged.connect( this, &RimVfpPlot::curveAppearanceChanged ); } } @@ -754,66 +645,41 @@ void RimVfpPlot::onPlotZoomed() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimVfpPlot::populatePlotData( const Opm::VFPProdTable& table, - RimVfpDefines::ProductionVariableType primaryVariable, - RimVfpDefines::ProductionVariableType familyVariable, - RimVfpDefines::InterpolatedVariableType interpolatedVariable, - RimVfpDefines::FlowingPhaseType flowingPhase, - VfpPlotData& plotData ) const +void RimVfpPlot::curveAppearanceChanged( const caf::SignalEmitter* emitter ) { - QString xAxisTitle = axisTitle( primaryVariable, flowingPhase ); - plotData.setXAxisTitle( xAxisTitle ); + scheduleReplot(); +} - QString yAxisTitle = QString( "%1 %2" ).arg( caf::AppEnum::uiText( interpolatedVariable ), - getDisplayUnitWithBracket( RimVfpDefines::ProductionVariableType::THP ) ); - plotData.setYAxisTitle( yAxisTitle ); +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimVfpPlot::initializeFromInitData( const VfpTableInitialData& table ) +{ + m_tableType = table.isProductionTable ? RimVfpDefines::TableType::PRODUCTION : RimVfpDefines::TableType::INJECTION; + m_tableNumber = table.tableNumber; + m_referenceDepth = table.datumDepth; + m_flowingPhase = table.flowingPhase; + m_flowingGasFraction = table.gasFraction; + m_flowingWaterFraction = table.waterFraction; +} - size_t numFamilyValues = getProductionTableData( table, familyVariable ).size(); - for ( size_t familyIdx = 0; familyIdx < numFamilyValues; familyIdx++ ) +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const RigVfpTables* RimVfpPlot::vfpTables() const +{ + if ( m_vfpTableData ) { - std::vector primaryAxisValues = getProductionTableData( table, primaryVariable ); - std::vector familyVariableValues = getProductionTableData( table, familyVariable ); - std::vector thpValues = getProductionTableData( table, RimVfpDefines::ProductionVariableType::THP ); - - size_t numValues = primaryAxisValues.size(); - std::vector yVals( numValues, 0.0 ); - - for ( size_t y = 0; y < numValues; y++ ) - { - size_t wfr_idx = - getVariableIndex( table, RimVfpDefines::ProductionVariableType::WATER_CUT, primaryVariable, y, familyVariable, familyIdx ); - size_t gfr_idx = - getVariableIndex( table, RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO, primaryVariable, y, familyVariable, familyIdx ); - size_t alq_idx = getVariableIndex( table, - RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY, - primaryVariable, - y, - familyVariable, - familyIdx ); - size_t flo_idx = - getVariableIndex( table, RimVfpDefines::ProductionVariableType::FLOW_RATE, primaryVariable, y, familyVariable, familyIdx ); - size_t thp_idx = - getVariableIndex( table, RimVfpDefines::ProductionVariableType::THP, primaryVariable, y, familyVariable, familyIdx ); - - yVals[y] = table( thp_idx, wfr_idx, gfr_idx, alq_idx, flo_idx ); - if ( m_interpolatedVariable == RimVfpDefines::InterpolatedVariableType::BHP_THP_DIFF ) - { - yVals[y] -= thpValues[thp_idx]; - } - } - - double familyValue = convertToDisplayUnit( familyVariableValues[familyIdx], familyVariable ); - QString familyUnit = getDisplayUnit( familyVariable ); - QString familyTitle = QString( "%1: %2 %3" ) - .arg( caf::AppEnum::uiText( familyVariable ) ) - .arg( familyValue ) - .arg( familyUnit ); - - convertToDisplayUnit( yVals, RimVfpDefines::ProductionVariableType::THP ); - convertToDisplayUnit( primaryAxisValues, primaryVariable ); - - plotData.appendCurve( familyTitle, primaryAxisValues, yVals ); + m_vfpTableData->ensureDataIsImported(); + return m_vfpTableData->vfpTables(); } + + if ( m_vfpTables ) + { + return m_vfpTables.get(); + } + + return nullptr; } //-------------------------------------------------------------------------------------------------- @@ -868,64 +734,12 @@ QString RimVfpPlot::getDisplayUnit( RimVfpDefines::ProductionVariableType variab return ""; } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -std::vector RimVfpPlot::getProductionTableData( const Opm::VFPProdTable& table, RimVfpDefines::ProductionVariableType variableType ) const -{ - std::vector xVals; - if ( variableType == RimVfpDefines::ProductionVariableType::WATER_CUT ) - { - xVals = table.getWFRAxis(); - } - else if ( variableType == RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO ) - { - xVals = table.getGFRAxis(); - } - else if ( variableType == RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY ) - { - xVals = table.getALQAxis(); - } - else if ( variableType == RimVfpDefines::ProductionVariableType::FLOW_RATE ) - { - xVals = table.getFloAxis(); - } - else if ( variableType == RimVfpDefines::ProductionVariableType::THP ) - { - xVals = table.getTHPAxis(); - } - - return xVals; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -size_t RimVfpPlot::getVariableIndex( const Opm::VFPProdTable& table, - RimVfpDefines::ProductionVariableType targetVariable, - RimVfpDefines::ProductionVariableType primaryVariable, - size_t primaryValue, - RimVfpDefines::ProductionVariableType familyVariable, - size_t familyValue ) const -{ - if ( targetVariable == primaryVariable ) return primaryValue; - if ( targetVariable == familyVariable ) return familyValue; - if ( targetVariable == RimVfpDefines::ProductionVariableType::WATER_CUT ) return m_waterCutIdx; - if ( targetVariable == RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO ) return m_gasLiquidRatioIdx; - if ( targetVariable == RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY ) return m_articifialLiftQuantityIdx; - if ( targetVariable == RimVfpDefines::ProductionVariableType::FLOW_RATE ) return m_flowRateIdx; - if ( targetVariable == RimVfpDefines::ProductionVariableType::THP ) return m_thpIdx; - - return getProductionTableData( table, targetVariable ).size() - 1; -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { - uiOrdering.add( &m_filePath ); - m_filePath.uiCapability()->setUiReadOnly( m_dataIsImportedExternally ); + uiOrdering.add( &m_vfpTableData ); uiOrdering.add( &m_tableType ); uiOrdering.add( &m_tableNumber ); @@ -999,89 +813,26 @@ QList RimVfpPlot::calculateValueOptions( const caf::PdmF calculateTableValueOptions( RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO, options ); } + else if ( fieldNeedingOptions == &m_vfpTableData ) + { + RimVfpDataCollection* vfpDataCollection = RimVfpDataCollection::instance(); + for ( auto table : vfpDataCollection->vfpTableData() ) + { + options.push_back( caf::PdmOptionItemInfo( table->name(), table ) ); + } + } + return options; } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RimVfpDefines::FlowingPhaseType RimVfpPlot::getFlowingPhaseType( const Opm::VFPProdTable& table ) -{ - switch ( table.getFloType() ) - { - case Opm::VFPProdTable::FLO_TYPE::FLO_OIL: - return RimVfpDefines::FlowingPhaseType::OIL; - case Opm::VFPProdTable::FLO_TYPE::FLO_GAS: - return RimVfpDefines::FlowingPhaseType::GAS; - case Opm::VFPProdTable::FLO_TYPE::FLO_LIQ: - return RimVfpDefines::FlowingPhaseType::LIQUID; - default: - return RimVfpDefines::FlowingPhaseType::INVALID; - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RimVfpDefines::FlowingPhaseType RimVfpPlot::getFlowingPhaseType( const Opm::VFPInjTable& table ) -{ - switch ( table.getFloType() ) - { - case Opm::VFPInjTable::FLO_TYPE::FLO_OIL: - return RimVfpDefines::FlowingPhaseType::OIL; - case Opm::VFPInjTable::FLO_TYPE::FLO_GAS: - return RimVfpDefines::FlowingPhaseType::GAS; - case Opm::VFPInjTable::FLO_TYPE::FLO_WAT: - return RimVfpDefines::FlowingPhaseType::WATER; - default: - return RimVfpDefines::FlowingPhaseType::INVALID; - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RimVfpDefines::FlowingGasFractionType RimVfpPlot::getFlowingGasFractionType( const Opm::VFPProdTable& table ) -{ - switch ( table.getGFRType() ) - { - case Opm::VFPProdTable::GFR_TYPE::GFR_GOR: - return RimVfpDefines::FlowingGasFractionType::GOR; - case Opm::VFPProdTable::GFR_TYPE::GFR_GLR: - return RimVfpDefines::FlowingGasFractionType::GLR; - case Opm::VFPProdTable::GFR_TYPE::GFR_OGR: - return RimVfpDefines::FlowingGasFractionType::OGR; - default: - return RimVfpDefines::FlowingGasFractionType::INVALID; - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RimVfpDefines::FlowingWaterFractionType RimVfpPlot::getFlowingWaterFractionType( const Opm::VFPProdTable& table ) -{ - switch ( table.getWFRType() ) - { - case Opm::VFPProdTable::WFR_TYPE::WFR_WOR: - return RimVfpDefines::FlowingWaterFractionType::WOR; - case Opm::VFPProdTable::WFR_TYPE::WFR_WCT: - return RimVfpDefines::FlowingWaterFractionType::WCT; - case Opm::VFPProdTable::WFR_TYPE::WFR_WGR: - return RimVfpDefines::FlowingWaterFractionType::WGR; - default: - return RimVfpDefines::FlowingWaterFractionType::INVALID; - } -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::calculateTableValueOptions( RimVfpDefines::ProductionVariableType variableType, QList& options ) { - if ( m_prodTable ) + if ( vfpTables() ) { - std::vector values = getProductionTableData( *m_prodTable, variableType ); + auto values = vfpTables()->getProductionTableData( m_tableNumber(), variableType ); for ( size_t i = 0; i < values.size(); i++ ) { @@ -1098,10 +849,32 @@ void RimVfpPlot::calculateTableValueOptions( RimVfpDefines::ProductionVariableTy void RimVfpPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) { RimPlot::fieldChangedByUi( changedField, oldValue, newValue ); + loadDataAndUpdate(); updateLayout(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimVfpPlot::initAfterRead() +{ + auto filePath = m_filePath_OBSOLETE.v().path(); + if ( filePath.isEmpty() ) return; + + QString fileName = RimProject::current()->updatedFilePathFromPathId( filePath ); + + auto vfpDataCollection = RimVfpDataCollection::instance(); + if ( vfpDataCollection ) + { + auto tableData = vfpDataCollection->appendTableDataObject( fileName ); + if ( tableData ) + { + setDataSource( tableData ); + } + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -1143,3 +916,22 @@ caf::PdmFieldHandle* RimVfpPlot::userDescriptionField() { return &m_plotTitle; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimVfpPlot::scheduleReplot() +{ + if ( m_plotWidget ) + { + m_plotWidget->scheduleReplot(); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +VfpTableSelection RimVfpPlot::tableSelection() const +{ + return { m_flowRateIdx(), m_thpIdx(), m_articifialLiftQuantityIdx(), m_waterCutIdx(), m_gasLiquidRatioIdx() }; +} diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlot.h b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlot.h index 71451ae6c3..7bf0192532 100644 --- a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlot.h +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlot.h @@ -22,15 +22,24 @@ #include "RimVfpDefines.h" #include "cafFilePath.h" +#include "cafPdmPtrField.h" #include -#include "opm/input/eclipse/Schedule/VFPInjTable.hpp" -#include "opm/input/eclipse/Schedule/VFPProdTable.hpp" - class RiuPlotWidget; class VfpPlotData; class RimPlotAxisProperties; +class RigVfpTables; +class RimVfpTableData; + +struct VfpTableSelection; +struct VfpTableInitialData; + +namespace Opm +{ +class VFPInjTable; +class VFPProdTable; +} // namespace Opm //-------------------------------------------------------------------------------------------------- /// Vertical Flow Performance Plot @@ -43,7 +52,9 @@ public: RimVfpPlot(); ~RimVfpPlot() override; - void setFileName( const QString& filename ); + void setDataSource( RimVfpTableData* vfpTableData ); + void setTableNumber( int tableNumber ); + void initializeObject(); // RimPlot implementations RiuPlotWidget* plotWidget() override; @@ -65,43 +76,30 @@ public: QImage snapshotWindowContent() override; void zoomAll() override; - void setProductionTable( const Opm::VFPProdTable& table ); - void setInjectionTable( const Opm::VFPInjTable& table ); void setDataIsImportedExternally( bool dataIsImportedExternally ); int tableNumber() const; private: - // RimPlot implementations - void doRemoveFromCollection(); - - // RimViewWindow implementations + void onChildrenUpdated( caf::PdmChildArrayFieldHandle* childArray, std::vector& updatedObjects ) override; void deleteViewWidget() override; void onLoadDataAndUpdate() override; - // PDM methods caf::PdmFieldHandle* userDescriptionField() override; + void scheduleReplot(); + private: - RiuPlotWidget* doCreatePlotViewWidget( QWidget* mainWindowParent ) override; - - void populatePlotWidgetWithCurveData( RiuPlotWidget* plotWidget, const Opm::VFPInjTable& table ); - void populatePlotWidgetWithCurveData( RiuPlotWidget* plotWidget, - const Opm::VFPProdTable& table, - RimVfpDefines::ProductionVariableType primaryVariable, - RimVfpDefines::ProductionVariableType familyVariable ); - std::vector getProductionTableData( const Opm::VFPProdTable& table, RimVfpDefines::ProductionVariableType variableType ) const; - size_t getVariableIndex( const Opm::VFPProdTable& table, - RimVfpDefines::ProductionVariableType targetVariable, - RimVfpDefines::ProductionVariableType primaryVariable, - size_t primaryValue, - RimVfpDefines::ProductionVariableType familyVariable, - size_t familyValue ) const; - void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override; void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override; - + void initAfterRead() override; QList calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override; + VfpTableSelection tableSelection() const; + void initializeFromInitData( const VfpTableInitialData& table ); + const RigVfpTables* vfpTables() const; + + RiuPlotWidget* doCreatePlotViewWidget( QWidget* mainWindowParent ) override; + void calculateTableValueOptions( RimVfpDefines::ProductionVariableType variableType, QList& options ); void setFixedVariableUiEditability( caf::PdmField& field, RimVfpDefines::ProductionVariableType variableType ); @@ -114,30 +112,11 @@ private: RimVfpDefines::ProductionVariableType primaryVariable, RimVfpDefines::ProductionVariableType familyVariable ); - static double convertToDisplayUnit( double value, RimVfpDefines::ProductionVariableType variableType ); - static void convertToDisplayUnit( std::vector& values, RimVfpDefines::ProductionVariableType variableType ); - + static double convertToDisplayUnit( double value, RimVfpDefines::ProductionVariableType variableType ); + static void convertToDisplayUnit( std::vector& values, RimVfpDefines::ProductionVariableType variableType ); static QString getDisplayUnit( RimVfpDefines::ProductionVariableType variableType ); - static QString getDisplayUnitWithBracket( RimVfpDefines::ProductionVariableType variableType ); - static RimVfpDefines::FlowingPhaseType getFlowingPhaseType( const Opm::VFPProdTable& table ); - static RimVfpDefines::FlowingPhaseType getFlowingPhaseType( const Opm::VFPInjTable& table ); - static RimVfpDefines::FlowingWaterFractionType getFlowingWaterFractionType( const Opm::VFPProdTable& table ); - static RimVfpDefines::FlowingGasFractionType getFlowingGasFractionType( const Opm::VFPProdTable& table ); - - void populatePlotData( const Opm::VFPProdTable& table, - RimVfpDefines::ProductionVariableType primaryVariable, - RimVfpDefines::ProductionVariableType familyVariable, - RimVfpDefines::InterpolatedVariableType interpolatedVariable, - RimVfpDefines::FlowingPhaseType flowingPhase, - VfpPlotData& plotData ) const; - - static void populatePlotData( const Opm::VFPInjTable& table, - RimVfpDefines::InterpolatedVariableType interpolatedVariable, - RimVfpDefines::FlowingPhaseType flowingPhase, - VfpPlotData& plotData ); - void populatePlotWidgetWithPlotData( RiuPlotWidget* plotWidget, const VfpPlotData& plotData ); static QString axisTitle( RimVfpDefines::ProductionVariableType variableType, RimVfpDefines::FlowingPhaseType flowingPhase ); @@ -149,10 +128,11 @@ private: void updateAxisRangesFromPlotWidget() override; void onPlotZoomed(); + void curveAppearanceChanged( const caf::SignalEmitter* emitter ); private: caf::PdmField m_plotTitle; - caf::PdmField m_filePath; + caf::PdmPtrField m_vfpTableData; caf::PdmField m_tableNumber; caf::PdmField m_referenceDepth; caf::PdmField> m_flowingPhase; @@ -173,9 +153,12 @@ private: caf::PdmChildField m_yAxisProperties; caf::PdmChildField m_xAxisProperties; - QPointer m_plotWidget; - std::unique_ptr m_prodTable; - std::unique_ptr m_injectionTable; + caf::PdmChildArrayField m_plotCurves; + + QPointer m_plotWidget; + + std::unique_ptr m_vfpTables; + caf::PdmField m_filePath_OBSOLETE; bool m_dataIsImportedExternally; }; diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlotCollection.cpp b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlotCollection.cpp index 05c78b96aa..734cd655a5 100644 --- a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlotCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlotCollection.cpp @@ -18,16 +18,8 @@ #include "RimVfpPlotCollection.h" -#include "RiaApplication.h" - -#include "RigCaseCellResultsData.h" -#include "RigEclipseCaseData.h" -#include "RigEclipseResultAddress.h" -#include "RigEquil.h" - -#include "RimEclipseResultCase.h" -#include "RimProject.h" #include "RimVfpDeck.h" +#include "RimVfpTableData.h" #include "cafCmdFeatureMenuBuilder.h" @@ -47,8 +39,43 @@ RimVfpPlotCollection::RimVfpPlotCollection() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RimVfpPlotCollection::~RimVfpPlotCollection() +RimVfpPlot* RimVfpPlotCollection::createAndAppendPlots( RimVfpTableData* tableData ) { + if ( !tableData ) return nullptr; + + tableData->ensureDataIsImported(); + + if ( !tableData->vfpTables() ) return nullptr; + + RimVfpPlot* firstPlot = nullptr; + + if ( tableData->tableCount() > 1 ) + { + auto* deck = new RimVfpDeck(); + deck->setDataSource( tableData ); + addDeck( deck ); + deck->loadDataAndUpdate(); + deck->updateAllRequiredEditors(); + + auto plots = deck->plots(); + if ( !plots.empty() ) + { + firstPlot = plots.front(); + } + } + else + { + auto vfpPlot = new RimVfpPlot(); + vfpPlot->setDataSource( tableData ); + vfpPlot->initializeObject(); + + addPlot( vfpPlot ); + vfpPlot->loadDataAndUpdate(); + + firstPlot = vfpPlot; + } + + return firstPlot; } //-------------------------------------------------------------------------------------------------- @@ -75,14 +102,6 @@ std::vector RimVfpPlotCollection::plots() const return m_vfpPlots.childrenByType(); } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimVfpPlotCollection::deleteChildren() -{ - m_vfpPlots.deleteChildren(); -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -119,13 +138,30 @@ void RimVfpPlotCollection::removePlot( RimVfpPlot* vfpPlot ) //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RimVfpDeck* RimVfpPlotCollection::addDeck( const QString& filename ) +void RimVfpPlotCollection::addDeck( RimVfpDeck* deck ) { - RimVfpDeck* deck = new RimVfpDeck(); - deck->setFileName( filename ); m_vfpDecks.push_back( deck ); +} - return deck; +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimVfpPlotCollection::deleteAllPlots() +{ + m_vfpPlots.deleteChildren(); + m_vfpDecks.deleteChildren(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimVfpPlotCollection::addImportItems( caf::CmdFeatureMenuBuilder& menuBuilder ) +{ + // A variant with a true value is used to indicate that the VFP data is imported from a file + // This is used to distinguish between VFP data imported from a file and VFP data imported from a simulator + QVariant variant( QVariant::fromValue( true ) ); + menuBuilder.addCmdFeatureWithUserData( "RicImportVfpDataFeature", "Import VFP Files", variant ); + menuBuilder.addCmdFeature( "RicImportVfpDataFeature", "Import VFP from Simulator Files" ); } //-------------------------------------------------------------------------------------------------- @@ -160,9 +196,5 @@ void RimVfpPlotCollection::loadDataAndUpdateAllPlots() //-------------------------------------------------------------------------------------------------- void RimVfpPlotCollection::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const { - // A variant with a true value is used to indicate that the VFP data is imported from a file - // This is used to distinguish between VFP data imported from a file and VFP data imported from a simulator - QVariant variant( QVariant::fromValue( true ) ); - menuBuilder.addCmdFeatureWithUserData( "RicImportVfpDataFeature", "Import VFP Files", variant ); - menuBuilder.addCmdFeature( "RicImportVfpDataFeature", "Import VFP from Simulator Files" ); + addImportItems( menuBuilder ); } diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlotCollection.h b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlotCollection.h index 916d974aa5..a489deccbd 100644 --- a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlotCollection.h +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlotCollection.h @@ -35,18 +35,19 @@ class RimVfpPlotCollection : public caf::PdmObject, public RimTypedPlotCollectio public: RimVfpPlotCollection(); - ~RimVfpPlotCollection() override; + + RimVfpPlot* createAndAppendPlots( RimVfpTableData* tableData ); + RimVfpPlot* plotForTableNumber( int tableNumber ) const; void addPlot( RimVfpPlot* newPlot ) override; std::vector plots() const override; - void deleteChildren(); - RimVfpPlot* plotForTableNumber( int tableNumber ) const; size_t plotCount() const final; void insertPlot( RimVfpPlot* vfpPlot, size_t index ) final; void removePlot( RimVfpPlot* vfpPlot ) final; + void deleteAllPlots() override; - RimVfpDeck* addDeck( const QString& filename ); + static void addImportItems( caf::CmdFeatureMenuBuilder& menuBuilder ); private: void loadDataAndUpdateAllPlots() override; @@ -54,6 +55,8 @@ private: void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override; + void addDeck( RimVfpDeck* deck ); + private: caf::PdmChildArrayField m_vfpPlots; caf::PdmChildArrayField m_vfpDecks; diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpTableData.cpp b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpTableData.cpp new file mode 100644 index 0000000000..39414adb27 --- /dev/null +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpTableData.cpp @@ -0,0 +1,126 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2024 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 "RimVfpTableData.h" + +#include "RiaOpmParserTools.h" + +#include "RigVfpTables.h" + +#include "cafCmdFeatureMenuBuilder.h" + +#include + +CAF_PDM_SOURCE_INIT( RimVfpTableData, "RimVfpTableData" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimVfpTableData::RimVfpTableData() +{ + CAF_PDM_InitObject( "VFP Plot", ":/VfpPlot.svg" ); + + CAF_PDM_InitFieldNoDefault( &m_filePath, "FilePath", "File Path" ); + + setDeletable( true ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimVfpTableData::setFileName( const QString& filename ) +{ + m_filePath = filename; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimVfpTableData::baseFileName() +{ + QFileInfo fileInfo( m_filePath().path() ); + return fileInfo.baseName(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimVfpTableData::ensureDataIsImported() +{ + if ( m_vfpTables ) return; + + updateObjectName(); + + m_vfpTables = std::make_unique(); + + const auto [vfpProdTables, vfpInjTables] = RiaOpmParserTools::extractVfpTablesFromDataFile( m_filePath().path().toStdString() ); + for ( const auto& prod : vfpProdTables ) + { + m_vfpTables->addProductionTable( prod ); + } + + for ( const auto& inj : vfpInjTables ) + { + m_vfpTables->addInjectionTable( inj ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +size_t RimVfpTableData::tableCount() const +{ + if ( m_vfpTables ) + { + return m_vfpTables->injectionTableNumbers().size() + m_vfpTables->productionTableNumbers().size(); + } + + return 0; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const RigVfpTables* RimVfpTableData::vfpTables() const +{ + return m_vfpTables.get(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimVfpTableData::updateObjectName() +{ + QString name = "VFP Plots"; + + QFileInfo fileInfo( m_filePath().path() ); + auto fileName = fileInfo.fileName(); + if ( !fileName.isEmpty() ) + { + name = fileName; + } + setName( name ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimVfpTableData::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const +{ + menuBuilder << "RicNewVfpPlotFeature"; +} diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpTableData.h b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpTableData.h new file mode 100644 index 0000000000..dd3abcb757 --- /dev/null +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpTableData.h @@ -0,0 +1,55 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2024 Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "RimNamedObject.h" + +#include "cafFilePath.h" + +#include + +class RigVfpTables; + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +class RimVfpTableData : public RimNamedObject +{ + CAF_PDM_HEADER_INIT; + +public: + RimVfpTableData(); + + void setFileName( const QString& filename ); + QString baseFileName(); + void ensureDataIsImported(); + + size_t tableCount() const; + + const RigVfpTables* vfpTables() const; + +private: + void updateObjectName(); + void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override; + +private: + caf::PdmField m_filePath; + + std::unique_ptr m_vfpTables; +}; diff --git a/ApplicationLibCode/ReservoirDataModel/CMakeLists_files.cmake b/ApplicationLibCode/ReservoirDataModel/CMakeLists_files.cmake index f37b87b270..924cadcb6b 100644 --- a/ApplicationLibCode/ReservoirDataModel/CMakeLists_files.cmake +++ b/ApplicationLibCode/ReservoirDataModel/CMakeLists_files.cmake @@ -98,6 +98,7 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RigWellResultBranch.h ${CMAKE_CURRENT_LIST_DIR}/RigWellResultFrame.h ${CMAKE_CURRENT_LIST_DIR}/RigReservoirBuilder.h + ${CMAKE_CURRENT_LIST_DIR}/RigVfpTables.h ) set(SOURCE_GROUP_SOURCE_FILES @@ -194,6 +195,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RigWellResultFrame.cpp ${CMAKE_CURRENT_LIST_DIR}/RigDeclineCurveCalculator.cpp ${CMAKE_CURRENT_LIST_DIR}/RigReservoirBuilder.cpp + ${CMAKE_CURRENT_LIST_DIR}/RigVfpTables.cpp ) list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES}) diff --git a/ApplicationLibCode/ReservoirDataModel/RigVfpTables.cpp b/ApplicationLibCode/ReservoirDataModel/RigVfpTables.cpp new file mode 100644 index 0000000000..b0e1fba717 --- /dev/null +++ b/ApplicationLibCode/ReservoirDataModel/RigVfpTables.cpp @@ -0,0 +1,569 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2024 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 "RigVfpTables.h" + +#include "RiaEclipseUnitTools.h" + +#include "cafAppEnum.h" + +#include "opm/input/eclipse/Schedule/VFPInjTable.hpp" +#include "opm/input/eclipse/Schedule/VFPProdTable.hpp" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +VfpPlotData RigVfpTables::populatePlotData( const Opm::VFPInjTable& table, + RimVfpDefines::InterpolatedVariableType interpolatedVariable, + RimVfpDefines::FlowingPhaseType flowingPhase ) +{ + VfpPlotData plotData; + + QString xAxisTitle = axisTitle( RimVfpDefines::ProductionVariableType::FLOW_RATE, flowingPhase ); + plotData.setXAxisTitle( xAxisTitle ); + + QString yAxisTitle = QString( "%1 %2" ).arg( caf::AppEnum::uiText( interpolatedVariable ), + getDisplayUnitWithBracket( RimVfpDefines::ProductionVariableType::THP ) ); + plotData.setYAxisTitle( yAxisTitle ); + + std::vector thpValues = table.getTHPAxis(); + + for ( size_t thp = 0; thp < thpValues.size(); thp++ ) + { + size_t numValues = table.getFloAxis().size(); + std::vector xVals = table.getFloAxis(); + std::vector yVals( numValues, 0.0 ); + for ( size_t y = 0; y < numValues; y++ ) + { + yVals[y] = table( thp, y ); + if ( interpolatedVariable == RimVfpDefines::InterpolatedVariableType::BHP_THP_DIFF ) + { + yVals[y] -= thpValues[thp]; + } + } + + double value = convertToDisplayUnit( thpValues[thp], RimVfpDefines::ProductionVariableType::THP ); + QString unit = getDisplayUnit( RimVfpDefines::ProductionVariableType::THP ); + QString title = QString( "%1 [%2]: %3" ) + .arg( caf::AppEnum::uiText( RimVfpDefines::ProductionVariableType::THP ) ) + .arg( unit ) + .arg( value ); + + convertToDisplayUnit( yVals, RimVfpDefines::ProductionVariableType::THP ); + convertToDisplayUnit( xVals, RimVfpDefines::ProductionVariableType::FLOW_RATE ); + + plotData.appendCurve( title, xVals, yVals ); + } + + return plotData; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +VfpPlotData RigVfpTables::populatePlotData( const Opm::VFPProdTable& table, + RimVfpDefines::ProductionVariableType primaryVariable, + RimVfpDefines::ProductionVariableType familyVariable, + RimVfpDefines::InterpolatedVariableType interpolatedVariable, + RimVfpDefines::FlowingPhaseType flowingPhase, + const VfpTableSelection& tableSelection ) +{ + VfpPlotData plotData; + + QString xAxisTitle = axisTitle( primaryVariable, flowingPhase ); + plotData.setXAxisTitle( xAxisTitle ); + + QString yAxisTitle = QString( "%1 %2" ).arg( caf::AppEnum::uiText( interpolatedVariable ), + getDisplayUnitWithBracket( RimVfpDefines::ProductionVariableType::THP ) ); + plotData.setYAxisTitle( yAxisTitle ); + + size_t numFamilyValues = getProductionTableData( table, familyVariable ).size(); + for ( size_t familyIdx = 0; familyIdx < numFamilyValues; familyIdx++ ) + { + std::vector primaryAxisValues = getProductionTableData( table, primaryVariable ); + std::vector familyVariableValues = getProductionTableData( table, familyVariable ); + std::vector thpValues = getProductionTableData( table, RimVfpDefines::ProductionVariableType::THP ); + + size_t numValues = primaryAxisValues.size(); + std::vector yVals( numValues, 0.0 ); + + for ( size_t y = 0; y < numValues; y++ ) + { + size_t wfr_idx = + getVariableIndex( table, RimVfpDefines::ProductionVariableType::WATER_CUT, primaryVariable, y, familyVariable, familyIdx, tableSelection ); + size_t gfr_idx = getVariableIndex( table, + RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO, + primaryVariable, + y, + familyVariable, + familyIdx, + tableSelection ); + size_t alq_idx = getVariableIndex( table, + RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY, + primaryVariable, + y, + familyVariable, + familyIdx, + tableSelection ); + size_t flo_idx = + getVariableIndex( table, RimVfpDefines::ProductionVariableType::FLOW_RATE, primaryVariable, y, familyVariable, familyIdx, tableSelection ); + size_t thp_idx = + getVariableIndex( table, RimVfpDefines::ProductionVariableType::THP, primaryVariable, y, familyVariable, familyIdx, tableSelection ); + + yVals[y] = table( thp_idx, wfr_idx, gfr_idx, alq_idx, flo_idx ); + if ( interpolatedVariable == RimVfpDefines::InterpolatedVariableType::BHP_THP_DIFF ) + { + yVals[y] -= thpValues[thp_idx]; + } + } + + double familyValue = convertToDisplayUnit( familyVariableValues[familyIdx], familyVariable ); + QString familyUnit = getDisplayUnit( familyVariable ); + QString familyTitle = QString( "%1: %2 %3" ) + .arg( caf::AppEnum::uiText( familyVariable ) ) + .arg( familyValue ) + .arg( familyUnit ); + + convertToDisplayUnit( yVals, RimVfpDefines::ProductionVariableType::THP ); + convertToDisplayUnit( primaryAxisValues, primaryVariable ); + + plotData.appendCurve( familyTitle, primaryAxisValues, yVals ); + } + + return plotData; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +VfpPlotData RigVfpTables::populatePlotData( int tableIndex, + RimVfpDefines::ProductionVariableType primaryVariable, + RimVfpDefines::ProductionVariableType familyVariable, + RimVfpDefines::InterpolatedVariableType interpolatedVariable, + RimVfpDefines::FlowingPhaseType flowingPhase, + const VfpTableSelection& tableSelection ) const +{ + auto prodTable = productionTable( tableIndex ); + if ( prodTable.has_value() ) + { + return populatePlotData( *prodTable, primaryVariable, familyVariable, interpolatedVariable, flowingPhase, tableSelection ); + }; + + auto injContainer = injectionTable( tableIndex ); + if ( injContainer.has_value() ) + { + return populatePlotData( *injContainer, interpolatedVariable, flowingPhase ); + }; + + return {}; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RigVfpTables::asciiDataForTable( int tableNumber, + RimVfpDefines::ProductionVariableType primaryVariable, + RimVfpDefines::ProductionVariableType familyVariable, + RimVfpDefines::InterpolatedVariableType interpolatedVariable, + RimVfpDefines::FlowingPhaseType flowingPhase, + const VfpTableSelection& tableSelection ) const +{ + VfpPlotData plotData; + auto prodTable = productionTable( tableNumber ); + if ( prodTable.has_value() ) + { + plotData = populatePlotData( *prodTable, primaryVariable, familyVariable, interpolatedVariable, flowingPhase, tableSelection ); + } + else + { + auto injTable = injectionTable( tableNumber ); + if ( injTable.has_value() ) + { + plotData = populatePlotData( *injTable, interpolatedVariable, flowingPhase ); + } + } + + return textForPlotData( plotData ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RigVfpTables::axisTitle( RimVfpDefines::ProductionVariableType variableType, RimVfpDefines::FlowingPhaseType flowingPhase ) +{ + QString title; + + if ( flowingPhase == RimVfpDefines::FlowingPhaseType::GAS ) + { + title = "Gas "; + } + else + { + title = "Liquid "; + } + title += QString( "%1 %2" ).arg( caf::AppEnum::uiText( variableType ), + getDisplayUnitWithBracket( variableType ) ); + + return title; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RigVfpTables::getDisplayUnit( RimVfpDefines::ProductionVariableType variableType ) +{ + if ( variableType == RimVfpDefines::ProductionVariableType::THP ) return "Bar"; + + if ( variableType == RimVfpDefines::ProductionVariableType::FLOW_RATE ) return "Sm3/day"; + + return ""; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RigVfpTables::getDisplayUnitWithBracket( RimVfpDefines::ProductionVariableType variableType ) +{ + QString unit = getDisplayUnit( variableType ); + if ( !unit.isEmpty() ) return QString( "[%1]" ).arg( unit ); + + return {}; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RigVfpTables::convertToDisplayUnit( double value, RimVfpDefines::ProductionVariableType variableType ) +{ + if ( variableType == RimVfpDefines::ProductionVariableType::THP ) + { + return RiaEclipseUnitTools::pascalToBar( value ); + } + + if ( variableType == RimVfpDefines::ProductionVariableType::FLOW_RATE ) + { + // Convert to m3/sec to m3/day + return value * static_cast( 24 * 60 * 60 ); + } + + return value; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RigVfpTables::convertToDisplayUnit( std::vector& values, RimVfpDefines::ProductionVariableType variableType ) +{ + for ( double& value : values ) + value = convertToDisplayUnit( value, variableType ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RigVfpTables::textForPlotData( const VfpPlotData& plotData ) +{ + QString dataText; + + if ( plotData.size() > 0 ) + { + // The curves should have same dimensions + const size_t curveSize = plotData.curveSize( 0 ); + + // Generate the headers for the columns + // First column is the primary variable + QString columnTitleLine( plotData.xAxisTitle() ); + + // Then one column per "family" + for ( size_t s = 0; s < plotData.size(); s++ ) + { + columnTitleLine.append( QString( "\t%1" ).arg( plotData.curveTitle( s ) ) ); + } + columnTitleLine.append( "\n" ); + + dataText.append( columnTitleLine ); + + // Add the rows: one row per primary variable value + for ( size_t idx = 0; idx < curveSize; idx++ ) + { + QString line; + + // First item on each line is the primary variable + line.append( QString( "%1" ).arg( plotData.xData( 0 )[idx] ) ); + + for ( size_t s = 0; s < plotData.size(); s++ ) + { + line.append( QString( "\t%1" ).arg( plotData.yData( s )[idx] ) ); + } + dataText.append( line ); + dataText.append( "\n" ); + } + } + + return dataText; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RigVfpTables::getProductionTableData( const Opm::VFPProdTable& table, RimVfpDefines::ProductionVariableType variableType ) +{ + std::vector xVals; + if ( variableType == RimVfpDefines::ProductionVariableType::WATER_CUT ) + { + xVals = table.getWFRAxis(); + } + else if ( variableType == RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO ) + { + xVals = table.getGFRAxis(); + } + else if ( variableType == RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY ) + { + xVals = table.getALQAxis(); + } + else if ( variableType == RimVfpDefines::ProductionVariableType::FLOW_RATE ) + { + xVals = table.getFloAxis(); + } + else if ( variableType == RimVfpDefines::ProductionVariableType::THP ) + { + xVals = table.getTHPAxis(); + } + + return xVals; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RigVfpTables::getProductionTableData( int tableIndex, RimVfpDefines::ProductionVariableType variableType ) const +{ + auto prodTable = productionTable( tableIndex ); + if ( prodTable.has_value() ) + { + return getProductionTableData( *prodTable, variableType ); + } + + return {}; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +size_t RigVfpTables::getVariableIndex( const Opm::VFPProdTable& table, + RimVfpDefines::ProductionVariableType targetVariable, + RimVfpDefines::ProductionVariableType primaryVariable, + size_t primaryValue, + RimVfpDefines::ProductionVariableType familyVariable, + size_t familyValue, + const VfpTableSelection& tableSelection ) +{ + if ( targetVariable == primaryVariable ) return primaryValue; + if ( targetVariable == familyVariable ) return familyValue; + if ( targetVariable == RimVfpDefines::ProductionVariableType::WATER_CUT ) return tableSelection.waterCutIdx; + if ( targetVariable == RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO ) return tableSelection.gasLiquidRatioIdx; + if ( targetVariable == RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY ) + return tableSelection.articifialLiftQuantityIdx; + if ( targetVariable == RimVfpDefines::ProductionVariableType::FLOW_RATE ) return tableSelection.flowRateIdx; + if ( targetVariable == RimVfpDefines::ProductionVariableType::THP ) return tableSelection.thpIdx; + + return getProductionTableData( table, targetVariable ).size() - 1; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::optional RigVfpTables::injectionTable( int tableNumber ) const +{ + for ( const auto& table : m_injectionTables ) + { + if ( table.getTableNum() == tableNumber ) + { + return table; + } + } + + return std::nullopt; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::optional RigVfpTables::productionTable( int tableNumber ) const +{ + for ( const auto& table : m_productionTables ) + { + if ( table.getTableNum() == tableNumber ) + { + return table; + } + } + + return std::nullopt; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimVfpDefines::FlowingPhaseType RigVfpTables::getFlowingPhaseType( const Opm::VFPProdTable& table ) +{ + switch ( table.getFloType() ) + { + case Opm::VFPProdTable::FLO_TYPE::FLO_OIL: + return RimVfpDefines::FlowingPhaseType::OIL; + case Opm::VFPProdTable::FLO_TYPE::FLO_GAS: + return RimVfpDefines::FlowingPhaseType::GAS; + case Opm::VFPProdTable::FLO_TYPE::FLO_LIQ: + return RimVfpDefines::FlowingPhaseType::LIQUID; + default: + return RimVfpDefines::FlowingPhaseType::INVALID; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimVfpDefines::FlowingPhaseType RigVfpTables::getFlowingPhaseType( const Opm::VFPInjTable& table ) +{ + switch ( table.getFloType() ) + { + case Opm::VFPInjTable::FLO_TYPE::FLO_OIL: + return RimVfpDefines::FlowingPhaseType::OIL; + case Opm::VFPInjTable::FLO_TYPE::FLO_GAS: + return RimVfpDefines::FlowingPhaseType::GAS; + case Opm::VFPInjTable::FLO_TYPE::FLO_WAT: + return RimVfpDefines::FlowingPhaseType::WATER; + default: + return RimVfpDefines::FlowingPhaseType::INVALID; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimVfpDefines::FlowingWaterFractionType RigVfpTables::getFlowingWaterFractionType( const Opm::VFPProdTable& table ) +{ + switch ( table.getWFRType() ) + { + case Opm::VFPProdTable::WFR_TYPE::WFR_WOR: + return RimVfpDefines::FlowingWaterFractionType::WOR; + case Opm::VFPProdTable::WFR_TYPE::WFR_WCT: + return RimVfpDefines::FlowingWaterFractionType::WCT; + case Opm::VFPProdTable::WFR_TYPE::WFR_WGR: + return RimVfpDefines::FlowingWaterFractionType::WGR; + default: + return RimVfpDefines::FlowingWaterFractionType::INVALID; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimVfpDefines::FlowingGasFractionType RigVfpTables::getFlowingGasFractionType( const Opm::VFPProdTable& table ) +{ + switch ( table.getGFRType() ) + { + case Opm::VFPProdTable::GFR_TYPE::GFR_GOR: + return RimVfpDefines::FlowingGasFractionType::GOR; + case Opm::VFPProdTable::GFR_TYPE::GFR_GLR: + return RimVfpDefines::FlowingGasFractionType::GLR; + case Opm::VFPProdTable::GFR_TYPE::GFR_OGR: + return RimVfpDefines::FlowingGasFractionType::OGR; + default: + return RimVfpDefines::FlowingGasFractionType::INVALID; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RigVfpTables::addInjectionTable( const Opm::VFPInjTable& table ) +{ + m_injectionTables.push_back( table ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RigVfpTables::addProductionTable( const Opm::VFPProdTable& table ) +{ + m_productionTables.push_back( table ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RigVfpTables::injectionTableNumbers() const +{ + std::vector tableNumbers; + + for ( const auto& table : m_injectionTables ) + { + tableNumbers.push_back( table.getTableNum() ); + } + + return tableNumbers; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RigVfpTables::productionTableNumbers() const +{ + std::vector tableNumbers; + + for ( const auto& table : m_productionTables ) + { + tableNumbers.push_back( table.getTableNum() ); + } + + return tableNumbers; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +VfpTableInitialData RigVfpTables::getTableInitialData( int tableIndex ) const +{ + auto prodTable = productionTable( tableIndex ); + if ( prodTable.has_value() ) + { + VfpTableInitialData initialData; + initialData.isProductionTable = true; + initialData.tableNumber = prodTable->getTableNum(); + initialData.datumDepth = prodTable->getDatumDepth(); + initialData.flowingPhase = getFlowingPhaseType( *prodTable ); + initialData.waterFraction = getFlowingWaterFractionType( *prodTable ); + initialData.gasFraction = getFlowingGasFractionType( *prodTable ); + + return initialData; + } + + auto injTable = injectionTable( tableIndex ); + if ( injTable.has_value() ) + { + VfpTableInitialData initialData; + initialData.isProductionTable = false; + initialData.tableNumber = injTable->getTableNum(); + initialData.datumDepth = injTable->getDatumDepth(); + initialData.flowingPhase = getFlowingPhaseType( *injTable ); + return initialData; + } + + return {}; +} diff --git a/ApplicationLibCode/ReservoirDataModel/RigVfpTables.h b/ApplicationLibCode/ReservoirDataModel/RigVfpTables.h new file mode 100644 index 0000000000..a417ff4837 --- /dev/null +++ b/ApplicationLibCode/ReservoirDataModel/RigVfpTables.h @@ -0,0 +1,157 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2024 Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "VerticalFlowPerformance/RimVfpDefines.h" + +#include + +#include +#include + +namespace Opm +{ +class VFPInjTable; +class VFPProdTable; +} // namespace Opm + +class VfpPlotData +{ +public: + void setXAxisTitle( const QString& xAxisTitle ) { m_xAxisTitle = xAxisTitle; } + void setYAxisTitle( const QString& yAxisTitle ) { m_yAxisTitle = yAxisTitle; } + + const QString& xAxisTitle() const { return m_xAxisTitle; } + const QString& yAxisTitle() const { return m_yAxisTitle; } + + void appendCurve( const QString& curveTitle, const std::vector& xData, const std::vector& yData ) + { + m_curveTitles.push_back( curveTitle ); + m_xData.push_back( xData ); + m_yData.push_back( yData ); + } + + const QString& curveTitle( size_t idx ) const { return m_curveTitles[idx]; } + + size_t size() const { return m_xData.size(); } + + size_t curveSize( size_t idx ) const { return m_xData[idx].size(); } + + const std::vector& xData( size_t idx ) const { return m_xData[idx]; } + const std::vector& yData( size_t idx ) const { return m_yData[idx]; } + +private: + QString m_xAxisTitle; + QString m_yAxisTitle; + std::vector m_curveTitles; + std::vector> m_xData; + std::vector> m_yData; +}; + +struct VfpTableSelection +{ + int flowRateIdx; + int thpIdx; + int articifialLiftQuantityIdx; + int waterCutIdx; + int gasLiquidRatioIdx; +}; + +struct VfpTableInitialData +{ + bool isProductionTable; + int tableNumber; + double datumDepth; + RimVfpDefines::FlowingPhaseType flowingPhase; + RimVfpDefines::FlowingWaterFractionType waterFraction; + RimVfpDefines::FlowingGasFractionType gasFraction; +}; + +//================================================================================================== +/// +//================================================================================================== +class RigVfpTables +{ +public: + void addInjectionTable( const Opm::VFPInjTable& table ); + void addProductionTable( const Opm::VFPProdTable& table ); + + std::vector injectionTableNumbers() const; + std::vector productionTableNumbers() const; + + VfpTableInitialData getTableInitialData( int tableIndex ) const; + + std::vector getProductionTableData( int tableIndex, RimVfpDefines::ProductionVariableType variableType ) const; + + VfpPlotData populatePlotData( int tableIndex, + RimVfpDefines::ProductionVariableType primaryVariable, + RimVfpDefines::ProductionVariableType familyVariable, + RimVfpDefines::InterpolatedVariableType interpolatedVariable, + RimVfpDefines::FlowingPhaseType flowingPhase, + const VfpTableSelection& tableSelection ) const; + + QString asciiDataForTable( int tableNumber, + RimVfpDefines::ProductionVariableType primaryVariable, + RimVfpDefines::ProductionVariableType familyVariable, + RimVfpDefines::InterpolatedVariableType interpolatedVariable, + RimVfpDefines::FlowingPhaseType flowingPhase, + const VfpTableSelection& tableSelection ) const; + +private: + static VfpPlotData populatePlotData( const Opm::VFPInjTable& table, + RimVfpDefines::InterpolatedVariableType interpolatedVariable, + RimVfpDefines::FlowingPhaseType flowingPhase ); + + static VfpPlotData populatePlotData( const Opm::VFPProdTable& table, + RimVfpDefines::ProductionVariableType primaryVariable, + RimVfpDefines::ProductionVariableType familyVariable, + RimVfpDefines::InterpolatedVariableType interpolatedVariable, + RimVfpDefines::FlowingPhaseType flowingPhase, + const VfpTableSelection& tableSelection ); + + static QString axisTitle( RimVfpDefines::ProductionVariableType variableType, RimVfpDefines::FlowingPhaseType flowingPhase ); + static QString getDisplayUnit( RimVfpDefines::ProductionVariableType variableType ); + static QString getDisplayUnitWithBracket( RimVfpDefines::ProductionVariableType variableType ); + + static double convertToDisplayUnit( double value, RimVfpDefines::ProductionVariableType variableType ); + static void convertToDisplayUnit( std::vector& values, RimVfpDefines::ProductionVariableType variableType ); + + static QString textForPlotData( const VfpPlotData& plotData ); + + static std::vector getProductionTableData( const Opm::VFPProdTable& table, RimVfpDefines::ProductionVariableType variableType ); + static size_t getVariableIndex( const Opm::VFPProdTable& table, + RimVfpDefines::ProductionVariableType targetVariable, + RimVfpDefines::ProductionVariableType primaryVariable, + size_t primaryValue, + RimVfpDefines::ProductionVariableType familyVariable, + size_t familyValue, + const VfpTableSelection& tableSelection ); + + std::optional injectionTable( int tableNumber ) const; + std::optional productionTable( int tableNumber ) const; + + static RimVfpDefines::FlowingPhaseType getFlowingPhaseType( const Opm::VFPProdTable& table ); + static RimVfpDefines::FlowingPhaseType getFlowingPhaseType( const Opm::VFPInjTable& table ); + static RimVfpDefines::FlowingWaterFractionType getFlowingWaterFractionType( const Opm::VFPProdTable& table ); + static RimVfpDefines::FlowingGasFractionType getFlowingGasFractionType( const Opm::VFPProdTable& table ); + +private: + std::vector m_injectionTables; + std::vector m_productionTables; +};