///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2020- 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 "RimVfpPlot.h" #include "RimEclipseResultCase.h" #include "RimFlowDiagSolution.h" #include "RimProject.h" #include "RimTools.h" #include "RimVfpTableExtractor.h" #include "RigEclipseCaseData.h" #include "RigTofWellDistributionCalculator.h" #include "RiaColorTools.h" #include "RiuQwtPlotWidget.h" #include "qwt_legend.h" #include "qwt_legend_label.h" #include "qwt_plot.h" #include "qwt_plot_curve.h" #include "qwt_symbol.h" #include #include #include #include #include "cvfDebugTimer.h" #include "cvfTrace.h" #include //================================================================================================== // // // //================================================================================================== CAF_PDM_SOURCE_INIT( RimVfpPlot, "VfpPlot" ); namespace caf { template <> void caf::AppEnum::setUp() { addItem( RimVfpPlot::TableType::INJECTION, "INJECTION", "Injection" ); addItem( RimVfpPlot::TableType::PRODUCTION, "PRODUCTION", "Production" ); setDefault( RimVfpPlot::TableType::INJECTION ); } template <> void caf::AppEnum::setUp() { addItem( RimVfpPlot::ProductionTableType::LIQUID_FLOW_RATE, "LIQUID_FLOW_RATE", "Liquid Flow Rate" ); addItem( RimVfpPlot::ProductionTableType::ARTIFICIAL_LIFT_QUANTITY, "ALQ", "Artificial Lift Quantity" ); addItem( RimVfpPlot::ProductionTableType::WATER_CUT, "WATER_CUT", "Water Cut" ); addItem( RimVfpPlot::ProductionTableType::GAS_LIQUID_RATIO, "GAS_LIQUID_RATIO", "Gas Liquid Ratio" ); setDefault( RimVfpPlot::ProductionTableType::LIQUID_FLOW_RATE ); } } // namespace caf //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimVfpPlot::RimVfpPlot() { // TODO: add icon CAF_PDM_InitObject( "VFP Plot", "", "", "" ); CAF_PDM_InitFieldNoDefault( &m_case, "Case", "Case", "", "", "" ); CAF_PDM_InitField( &m_wellName, "WellName", QString( "None" ), "Well", "", "", "" ); caf::AppEnum defaultTableType = RimVfpPlot::TableType::INJECTION; CAF_PDM_InitField( &m_tableType, "TableType", defaultTableType, "Table Type", "", "", "" ); caf::AppEnum defaultProductionTableType = RimVfpPlot::ProductionTableType::LIQUID_FLOW_RATE; CAF_PDM_InitField( &m_productionTableType, "ProductionTableType", defaultProductionTableType, "Production Table Type", "", "", "" ); m_showWindow = false; m_showPlotLegends = true; setAsPlotMdiWindow(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimVfpPlot::~RimVfpPlot() { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::setDataSourceParameters( RimEclipseResultCase* eclipseResultCase, QString targetWellName ) { m_case = eclipseResultCase; m_wellName = targetWellName; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuQwtPlotWidget* RimVfpPlot::viewer() { return m_plotWidget; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::setAutoScaleXEnabled( bool /*enabled*/ ) { cvf::Trace::show( "RimVfpPlot::setAutoScaleXEnabled()" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::setAutoScaleYEnabled( bool /*enabled*/ ) { cvf::Trace::show( "RimVfpPlot::setAutoScaleYEnabled()" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::updateAxes() { cvf::Trace::show( "RimVfpPlot::updateAxes()" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::updateLegend() { if ( !m_plotWidget ) { return; } // Hide the legend when in multiplot mode, as the legend is handeled by the multi plot grid layout bool doShowLegend = false; if ( isMdiWindow() ) { doShowLegend = m_showPlotLegends; } if ( doShowLegend ) { QwtLegend* legend = new QwtLegend( m_plotWidget ); m_plotWidget->insertLegend( legend, QwtPlot::BottomLegend ); } else { m_plotWidget->insertLegend( nullptr ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::updateZoomInQwt() { cvf::Trace::show( "RimVfpPlot::updateZoomInQwt()" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::updateZoomFromQwt() { cvf::Trace::show( "RimVfpPlot::updateZoomFromQwt()" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimVfpPlot::asciiDataForPlotExport() const { cvf::Trace::show( "RimVfpPlot::asciiDataForPlotExport()" ); return QString(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::reattachAllCurves() { cvf::Trace::show( "RimVfpPlot::reattachAllCurves()" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::detachAllCurves() { cvf::Trace::show( "RimVfpPlot::detachAllCurves()" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- caf::PdmObject* RimVfpPlot::findPdmObjectFromQwtCurve( const QwtPlotCurve* /*curve*/ ) const { cvf::Trace::show( "RimVfpPlot::findPdmObjectFromQwtCurve()" ); return nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::onAxisSelected( int /*axis*/, bool /*toggle*/ ) { cvf::Trace::show( "RimVfpPlot::onAxisSelected()" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimVfpPlot::description() const { return uiName(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QWidget* RimVfpPlot::viewWidget() { cvf::Trace::show( "RimVfpPlot::viewWidget()" ); return m_plotWidget; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QImage RimVfpPlot::snapshotWindowContent() { cvf::Trace::show( "RimVfpPlot::snapshotWindowContent()" ); QImage image; if ( m_plotWidget ) { QPixmap pix = m_plotWidget->grab(); image = pix.toImage(); } return image; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::zoomAll() { cvf::Trace::show( "RimVfpPlot::zoomAll()" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::doRemoveFromCollection() { cvf::Trace::show( "RimVfpPlot::doRemoveFromCollection()" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuQwtPlotWidget* RimVfpPlot::doCreatePlotViewWidget( QWidget* mainWindowParent ) { cvf::Trace::show( "RimVfpPlot::createViewWidget()" ); // It seems we risk being called multiple times if ( m_plotWidget ) { return m_plotWidget; } m_plotWidget = new RiuQwtPlotWidget( this, mainWindowParent ); // m_plotWidget->setAutoReplot( false ); updateLegend(); onLoadDataAndUpdate(); return m_plotWidget; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::deleteViewWidget() { cvf::Trace::show( "RimVfpPlot::deleteViewWidget()" ); if ( m_plotWidget ) { m_plotWidget->setParent( nullptr ); delete m_plotWidget; m_plotWidget = nullptr; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::onLoadDataAndUpdate() { cvf::Trace::show( "RimVfpPlot::onLoadDataAndUpdate()" ); cvf::DebugTimer tim( "RimVfpPlot::onLoadDataAndUpdate()" ); if ( isMdiWindow() ) { updateMdiWindowVisibility(); } else { updateParentLayout(); } if ( !m_plotWidget ) { return; } m_plotWidget->detachItems( QwtPlotItem::Rtti_PlotCurve ); updateLegend(); QString phaseString = "N/A"; bool isInjector = m_tableType == RimVfpPlot::TableType::INJECTION; if ( m_case && m_case->ensureReservoirCaseIsOpen() ) { if ( isInjector ) { std::set wells = {"F2H", "C1H", "C2H", "C3H", "C4AH", "C4H", "F1H", "F3H", "F4H"}; std::string strippedWellName = QString( m_wellName() ).remove( "-" ).toStdString(); if ( wells.find( strippedWellName ) != wells.end() ) { QString gridFileName = m_case->gridFileName(); QFileInfo fi( gridFileName ); std::string filename = fi.canonicalPath().toStdString() + "/INCLUDE/VFP/" + strippedWellName + ".Ecl"; const std::vector tables = RimVfpTableExtractor::extractVfpInjectionTables( filename ); populatePlotWidgetWithCurveData( m_plotWidget, tables ); } } else { std::set wells = {"B1BH", "B2H", "B3H", "D1CH", "B4DH", "E1H", "D2H", "D3BH", "E3CH"}; std::string strippedWellName = QString( m_wellName() ).remove( "-" ).toStdString(); if ( wells.find( strippedWellName ) != wells.end() ) { QString gridFileName = m_case->gridFileName(); QFileInfo fi( gridFileName ); std::string filename = fi.canonicalPath().toStdString() + "/INCLUDE/VFP/" + strippedWellName + ".Ecl"; const std::vector tables = RimVfpTableExtractor::extractVfpProductionTables( filename ); populatePlotWidgetWithCurveData( m_plotWidget, tables, m_productionTableType() ); } } } const QString plotTitleStr = QString( "%1 Vertical Flow Performance Plot" ).arg( m_wellName ); m_plotWidget->setTitle( plotTitleStr ); if ( isInjector ) { m_plotWidget->setAxisTitleText( QwtPlot::xBottom, "Liquid Flow Rate [sm3/d]" ); m_plotWidget->setAxisTitleText( QwtPlot::yLeft, "Bottom Hole Pressure [Bar]" ); } else { m_plotWidget->setAxisTitleText( QwtPlot::xBottom, "x axis (todo) [x axis unit]" ); m_plotWidget->setAxisTitleText( QwtPlot::yLeft, "y axis [y axis unit]" ); } m_plotWidget->setAxisTitleEnabled( QwtPlot::xBottom, true ); m_plotWidget->setAxisTitleEnabled( QwtPlot::yLeft, true ); m_plotWidget->scheduleReplot(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::populatePlotWidgetWithCurveData( RiuQwtPlotWidget* plotWidget, const std::vector& tables ) { cvf::Trace::show( "RimVfpPlot::populatePlotWidgetWithCurves()" ); plotWidget->detachItems( QwtPlotItem::Rtti_PlotCurve ); plotWidget->setAxisScale( QwtPlot::xBottom, 0, 1 ); plotWidget->setAxisScale( QwtPlot::yLeft, 0, 1 ); plotWidget->setAxisAutoScale( QwtPlot::xBottom, true ); plotWidget->setAxisAutoScale( QwtPlot::yLeft, true ); size_t numTables = tables.size(); for ( size_t i = 0; i < numTables; i++ ) { const Opm::VFPInjTable table = tables[i]; std::cout << "Datum depth: " << table.getDatumDepth() << std::endl; std::cout << "Table number: " << table.getTableNum() << std::endl; std::cout << "Flow type: " << static_cast( table.getFloType() ) << std::endl; std::cout << "Flo axis: " << table.getFloAxis().size() << std::endl; std::cout << "THP axis: " << table.getTHPAxis().size() << std::endl; std::cout << "THP Axis:\n"; for ( size_t x = 0; x < table.getTHPAxis().size(); x++ ) { std::cout << " " << table.getTHPAxis()[x]; } std::cout << "\n"; for ( size_t y = 0; y < table.getFloAxis().size(); y++ ) { for ( size_t x = 0; x < table.getTHPAxis().size(); x++ ) { std::cout << " " << table( x, y ); } std::cout << std::endl; } for ( size_t thp = 0; thp < table.getTHPAxis().size(); thp++ ) { // Just create some dummy values for now 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++ ) { // Convert from Pascal to Bar yVals[y] = table( thp, y ) / 100000.0; } cvf::Color3f cvfClr = cvf::Color3::BLUE; QColor qtClr = RiaColorTools::toQColor( cvfClr ); QwtPlotCurve* curve = new QwtPlotCurve; // Convert from Pascal to Bar curve->setTitle( QString( "THP: %1 Bar" ).arg( table.getTHPAxis()[thp] / 100000.0 ) ); QwtSymbol* symbol = new QwtSymbol( QwtSymbol::Ellipse, QBrush( qtClr ), QPen( Qt::red, 2 ), QSize( 8, 8 ) ); curve->setSymbol( symbol ); curve->setSamples( xVals.data(), yVals.data(), numValues ); curve->attach( plotWidget ); curve->show(); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::populatePlotWidgetWithCurveData( RiuQwtPlotWidget* plotWidget, const std::vector& tables, RimVfpPlot::ProductionTableType productionTableType ) { cvf::Trace::show( "RimVfpPlot::populatePlotWidgetWithCurves()" ); plotWidget->detachItems( QwtPlotItem::Rtti_PlotCurve ); plotWidget->setAxisScale( QwtPlot::xBottom, 0, 1 ); plotWidget->setAxisScale( QwtPlot::yLeft, 0, 1 ); plotWidget->setAxisAutoScale( QwtPlot::xBottom, true ); plotWidget->setAxisAutoScale( QwtPlot::yLeft, true ); size_t numTables = tables.size(); for ( size_t i = 0; i < numTables; i++ ) { const Opm::VFPProdTable table = tables[i]; std::cout << "Datum depth: " << table.getDatumDepth() << std::endl; std::cout << "Table number: " << table.getTableNum() << std::endl; std::cout << "Flow type: " << static_cast( table.getFloType() ) << std::endl; std::cout << "Flo axis: " << table.getFloAxis().size() << std::endl; std::cout << "THP axis: " << table.getTHPAxis().size() << std::endl; std::cout << "WFR axis: " << table.getWFRAxis().size() << std::endl; std::cout << "GFR axis: " << table.getGFRAxis().size() << std::endl; std::cout << "ALQ axis: " << table.getALQAxis().size() << std::endl; std::cout << "THP Axis:\n"; for ( size_t x = 0; x < table.getTHPAxis().size(); x++ ) { std::cout << " " << table.getTHPAxis()[x]; } std::cout << "\n"; for ( size_t thp_idx = 0; thp_idx < table.getTHPAxis().size(); thp_idx++ ) { size_t wfr_idx = table.getWFRAxis().size() - 1; size_t gfr_idx = table.getGFRAxis().size() - 1; size_t alq_idx = table.getALQAxis().size() - 1; size_t flo_idx = table.getFloAxis().size() - 1; std::vector xVals; if ( productionTableType == RimVfpPlot::ProductionTableType::WATER_CUT ) { xVals = table.getWFRAxis(); } else if ( productionTableType == RimVfpPlot::ProductionTableType::GAS_LIQUID_RATIO ) { xVals = table.getGFRAxis(); } else if ( productionTableType == RimVfpPlot::ProductionTableType::ARTIFICIAL_LIFT_QUANTITY ) { xVals = table.getALQAxis(); } else if ( productionTableType == RimVfpPlot::ProductionTableType::LIQUID_FLOW_RATE ) { xVals = table.getFloAxis(); } size_t numValues = xVals.size(); std::vector yVals( numValues, 0.0 ); for ( size_t y = 0; y < numValues; y++ ) { if ( productionTableType == RimVfpPlot::ProductionTableType::WATER_CUT ) wfr_idx = y; else if ( productionTableType == RimVfpPlot::ProductionTableType::GAS_LIQUID_RATIO ) gfr_idx = y; else if ( productionTableType == RimVfpPlot::ProductionTableType::ARTIFICIAL_LIFT_QUANTITY ) alq_idx = y; else if ( productionTableType == RimVfpPlot::ProductionTableType::LIQUID_FLOW_RATE ) flo_idx = y; // Convert from Pascal to Bar yVals[y] = table( thp_idx, wfr_idx, gfr_idx, alq_idx, flo_idx ) / 100000.0; } cvf::Color3f cvfClr = cvf::Color3::BLUE; QColor qtClr = RiaColorTools::toQColor( cvfClr ); QwtPlotCurve* curve = new QwtPlotCurve; // Convert from Pascal to Bar curve->setTitle( QString( "THP: %1 Bar" ).arg( table.getTHPAxis()[thp_idx] / 100000.0 ) ); QwtSymbol* symbol = new QwtSymbol( QwtSymbol::Ellipse, QBrush( qtClr ), QPen( Qt::red, 2 ), QSize( 8, 8 ) ); curve->setSymbol( symbol ); curve->setSamples( xVals.data(), yVals.data(), numValues ); curve->attach( plotWidget ); curve->show(); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { uiOrdering.add( &m_case ); uiOrdering.add( &m_wellName ); uiOrdering.add( &m_tableType ); uiOrdering.add( &m_productionTableType ); m_productionTableType.uiCapability()->setUiHidden( m_tableType != RimVfpPlot::TableType::PRODUCTION ); uiOrdering.skipRemainingFields( true ); // RimPlot::defineUiOrdering( uiConfigName, uiOrdering ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QList RimVfpPlot::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly ) { QList options = RimPlot::calculateValueOptions( fieldNeedingOptions, useOptionsOnly ); if ( fieldNeedingOptions == &m_case ) { RimProject* ownerProj = nullptr; firstAncestorOrThisOfType( ownerProj ); if ( ownerProj ) { std::vector caseArr; ownerProj->descendantsIncludingThisOfType( caseArr ); for ( RimEclipseResultCase* c : caseArr ) { options.push_back( caf::PdmOptionItemInfo( c->caseUserDescription(), c, true, c->uiIconProvider() ) ); } } } else if ( fieldNeedingOptions == &m_wellName ) { if ( m_case && m_case->eclipseCaseData() ) { caf::IconProvider simWellIcon( ":/Well.png" ); const std::set sortedWellNameSet = m_case->eclipseCaseData()->findSortedWellNames(); for ( const QString& name : sortedWellNameSet ) { options.push_back( caf::PdmOptionItemInfo( name, name, true, simWellIcon ) ); } } if ( options.size() == 0 ) { options.push_back( caf::PdmOptionItemInfo( "None", QVariant() ) ); } } return options; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) { RimPlot::fieldChangedByUi( changedField, oldValue, newValue ); if ( changedField == &m_case ) { fixupDependentFieldsAfterCaseChange(); } loadDataAndUpdate(); updateLayout(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimVfpPlot::fixupDependentFieldsAfterCaseChange() { QString newWellName; if ( m_case ) { const std::set sortedWellNameSet = m_case->eclipseCaseData()->findSortedWellNames(); if ( sortedWellNameSet.size() > 0 ) { newWellName = *sortedWellNameSet.begin(); } } m_wellName = newWellName; }