From fe63231db9d2363eca58da122990c2ccedc706f3 Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Wed, 12 Jun 2024 07:52:37 +0200 Subject: [PATCH] Add support for multiple curve sets in one VFP plot - support selection of multiple values for multiple producer variables - use one color for curves in a curve set representing a VFP curve collection - use symbols to indicate individual family values for curves - show all required values to identify curves as curve legend text and curve mouse hover text - make sure all available settings of axis property object is applied to the plot axis - support display of all curve data using "Show Plot Data" --- .../RicShowPlotDataFeature.cpp | 3 +- .../RimPlotAxisProperties.cpp | 14 + .../ProjectDataModel/RimPlotAxisProperties.h | 2 + .../Summary/RimSummaryPlotAxisFormatter.cpp | 86 +-- .../Tools/RimPlotAxisTools.cpp | 121 +++- .../ProjectDataModel/Tools/RimPlotAxisTools.h | 3 + .../RimCustomVfpPlot.cpp | 670 ++++++++++++++---- .../RimCustomVfpPlot.h | 79 ++- .../VerticalFlowPerformance/RimVfpDefines.cpp | 2 +- .../RimVfpPlotCollection.cpp | 1 + .../ReservoirDataModel/RigVfpTables.cpp | 6 +- .../ReservoirDataModel/RigVfpTables.h | 2 +- 12 files changed, 725 insertions(+), 264 deletions(-) diff --git a/ApplicationLibCode/Commands/ApplicationCommands/RicShowPlotDataFeature.cpp b/ApplicationLibCode/Commands/ApplicationCommands/RicShowPlotDataFeature.cpp index fa142c7b25..a837ab7e22 100644 --- a/ApplicationLibCode/Commands/ApplicationCommands/RicShowPlotDataFeature.cpp +++ b/ApplicationLibCode/Commands/ApplicationCommands/RicShowPlotDataFeature.cpp @@ -37,6 +37,7 @@ #include "RimWellLogPlot.h" #include "RimWellLogTrack.h" +#include "VerticalFlowPerformance/RimCustomVfpPlot.h" #include "VerticalFlowPerformance/RimVfpPlot.h" #include "RiuPlotMainWindow.h" @@ -193,7 +194,7 @@ bool RicShowPlotDataFeature::isCommandEnabled() const for ( auto plot : selection ) { if ( dynamic_cast( plot ) || dynamic_cast( plot ) || dynamic_cast( plot ) || - dynamic_cast( plot ) || dynamic_cast( plot ) || + dynamic_cast( plot ) || dynamic_cast( plot ) || dynamic_cast( plot ) || dynamic_cast( plot ) || dynamic_cast( plot ) || dynamic_cast( plot ) || dynamic_cast( plot ) || dynamic_cast( plot ) ) diff --git a/ApplicationLibCode/ProjectDataModel/RimPlotAxisProperties.cpp b/ApplicationLibCode/ProjectDataModel/RimPlotAxisProperties.cpp index 25060d3655..93833a2fda 100644 --- a/ApplicationLibCode/ProjectDataModel/RimPlotAxisProperties.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimPlotAxisProperties.cpp @@ -107,6 +107,20 @@ RimPlotAxisProperties::RimPlotAxisProperties() updateOptionSensitivity(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimPlotAxisProperties::configureForBasicUse() +{ + setEnableTitleTextSettings( false ); + + m_isLogarithmicScaleEnabled.uiCapability()->setUiHidden( true ); + m_isAxisInverted.uiCapability()->setUiHidden( true ); + m_showNumbers.uiCapability()->setUiHidden( true ); + m_majorTickmarkCount.uiCapability()->setUiHidden( true ); + m_plotAxis.uiCapability()->setUiHidden( true ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/RimPlotAxisProperties.h b/ApplicationLibCode/ProjectDataModel/RimPlotAxisProperties.h index bfde0844d8..21ab776fc0 100644 --- a/ApplicationLibCode/ProjectDataModel/RimPlotAxisProperties.h +++ b/ApplicationLibCode/ProjectDataModel/RimPlotAxisProperties.h @@ -64,6 +64,8 @@ public: public: RimPlotAxisProperties(); + void configureForBasicUse(); + void setAlwaysRequired( bool enable ); void setEnableTitleTextSettings( bool enable ); diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlotAxisFormatter.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlotAxisFormatter.cpp index aa1a1de7c6..3ad7f60911 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlotAxisFormatter.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlotAxisFormatter.cpp @@ -29,73 +29,16 @@ #include "RimSummaryCase.h" #include "RimSummaryCaseCollection.h" #include "RimSummaryCurve.h" +#include "Tools/RimPlotAxisTools.h" #include "RiuQtChartsPlotWidget.h" -#include "RiuQwtPlotTools.h" #include "RiuSummaryQuantityNameInfoProvider.h" #include "RiuSummaryQwtPlot.h" -#include "qwt_date_scale_engine.h" -#include "qwt_plot.h" -#include "qwt_plot_curve.h" -#include "qwt_scale_draw.h" -#include "qwt_text.h" - #include #include #include -//-------------------------------------------------------------------------------------------------- -// e format as [-]9.9e[+|-]999 -// E format as[-]9.9E[+| -]999 -// f format as[-]9.9 -// g use e or f format, whichever is the most concise -// G use E or f format, whichever is the most concise - -//-------------------------------------------------------------------------------------------------- -class SummaryScaleDraw : public QwtScaleDraw -{ -public: - SummaryScaleDraw( double scaleFactor, - int numberOfDecimals, - RimPlotAxisProperties::NumberFormatType numberFormat = RimPlotAxisProperties::NUMBER_FORMAT_AUTO ) - { - m_scaleFactor = scaleFactor; - m_numberOfDecimals = numberOfDecimals; - m_numberFormat = numberFormat; - } - - QwtText label( double value ) const override - { - if ( qFuzzyCompare( scaledValue( value ) + 1.0, 1.0 ) ) value = 0.0; - - return QString::number( scaledValue( value ), numberFormat(), m_numberOfDecimals ); - } - -private: - char numberFormat() const - { - switch ( m_numberFormat ) - { - case RimPlotAxisProperties::NUMBER_FORMAT_AUTO: - return 'g'; - case RimPlotAxisProperties::NUMBER_FORMAT_DECIMAL: - return 'f'; - case RimPlotAxisProperties::NUMBER_FORMAT_SCIENTIFIC: - return 'e'; - default: - return 'g'; - } - } - - double scaledValue( double value ) const { return value / m_scaleFactor; } - -private: - double m_scaleFactor; - int m_numberOfDecimals; - RimPlotAxisProperties::NumberFormatType m_numberFormat; -}; - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -139,24 +82,7 @@ void RimSummaryPlotAxisFormatter::applyAxisPropertiesToPlot( RiuPlotWidget* plot plotWidget->setAxisTitleEnabled( axis, true ); } - auto qwtPlotWidget = dynamic_cast( plotWidget ); - if ( qwtPlotWidget ) - { - auto qwtAxisId = qwtPlotWidget->toQwtPlotAxis( axis ); - - if ( m_axisProperties->numberFormat() == RimPlotAxisProperties::NUMBER_FORMAT_AUTO && m_axisProperties->scaleFactor() == 1.0 ) - { - // Default to Qwt's own scale draw to avoid changing too much for default values - qwtPlotWidget->qwtPlot()->setAxisScaleDraw( qwtAxisId, new QwtScaleDraw ); - } - else - { - qwtPlotWidget->qwtPlot()->setAxisScaleDraw( qwtAxisId, - new SummaryScaleDraw( m_axisProperties->scaleFactor(), - m_axisProperties->decimalCount(), - m_axisProperties->numberFormat() ) ); - } - } + RimPlotAxisTools::applyAxisScaleDraw( plotWidget, axis, m_axisProperties ); #ifdef USE_QTCHARTS auto qtChartsPlotWidget = dynamic_cast( plotWidget ); @@ -307,13 +233,7 @@ QString RimSummaryPlotAxisFormatter::autoAxisTitle() const } QString assembledYAxisText; - QString scaleFactorText = ""; - - if ( m_axisProperties->scaleFactor() != 1.0 ) - { - int exponent = std::log10( m_axisProperties->scaleFactor() ); - scaleFactorText = QString( " x 10%1 " ).arg( QString::number( exponent ) ); - } + QString scaleFactorText = RimPlotAxisTools::scaleFactorText( m_axisProperties ); for ( const auto& unitIt : unitToQuantityNameMap ) { diff --git a/ApplicationLibCode/ProjectDataModel/Tools/RimPlotAxisTools.cpp b/ApplicationLibCode/ProjectDataModel/Tools/RimPlotAxisTools.cpp index 37f91cf370..3d21295c39 100644 --- a/ApplicationLibCode/ProjectDataModel/Tools/RimPlotAxisTools.cpp +++ b/ApplicationLibCode/ProjectDataModel/Tools/RimPlotAxisTools.cpp @@ -17,19 +17,80 @@ ///////////////////////////////////////////////////////////////////////////////// #include "RimPlotAxisTools.h" + #include "RimPlotAxisLogRangeCalculator.h" #include "RimPlotAxisProperties.h" #include "RimPlotCurve.h" +#include "RimSummaryPlotAxisFormatter.h" #include "RiuPlotAxis.h" #include "RiuPlotWidget.h" +#include "RiuQwtPlotWidget.h" + +#include "qwt_plot.h" +#include "qwt_scale_draw.h" +#include "qwt_text.h" + +#include + +namespace RimPlotAxisTools +{ + +//-------------------------------------------------------------------------------------------------- +// e format as [-]9.9e[+|-]999 +// E format as[-]9.9E[+| -]999 +// f format as[-]9.9 +// g use e or f format, whichever is the most concise +// G use E or f format, whichever is the most concise + +//-------------------------------------------------------------------------------------------------- +class SummaryScaleDraw : public QwtScaleDraw +{ +public: + SummaryScaleDraw( double scaleFactor, + int numberOfDecimals, + RimPlotAxisProperties::NumberFormatType numberFormat = RimPlotAxisProperties::NUMBER_FORMAT_AUTO ) + { + m_scaleFactor = scaleFactor; + m_numberOfDecimals = numberOfDecimals; + m_numberFormat = numberFormat; + } + + QwtText label( double value ) const override + { + if ( qFuzzyCompare( scaledValue( value ) + 1.0, 1.0 ) ) value = 0.0; + + return QString::number( scaledValue( value ), numberFormat(), m_numberOfDecimals ); + } + +private: + char numberFormat() const + { + switch ( m_numberFormat ) + { + case RimPlotAxisProperties::NUMBER_FORMAT_AUTO: + return 'g'; + case RimPlotAxisProperties::NUMBER_FORMAT_DECIMAL: + return 'f'; + case RimPlotAxisProperties::NUMBER_FORMAT_SCIENTIFIC: + return 'e'; + default: + return 'g'; + } + } + + double scaledValue( double value ) const { return value / m_scaleFactor; } + +private: + double m_scaleFactor; + int m_numberOfDecimals; + RimPlotAxisProperties::NumberFormatType m_numberFormat; +}; //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimPlotAxisTools::updateVisibleRangesFromPlotWidget( RimPlotAxisProperties* axisProperties, - RiuPlotAxis plotAxis, - const RiuPlotWidget* const plotWidget ) +void updateVisibleRangesFromPlotWidget( RimPlotAxisProperties* axisProperties, RiuPlotAxis plotAxis, const RiuPlotWidget* const plotWidget ) { if ( !plotWidget || !axisProperties ) return; @@ -44,11 +105,11 @@ void RimPlotAxisTools::updateVisibleRangesFromPlotWidget( RimPlotAxisProperties* //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimPlotAxisTools::updatePlotWidgetFromAxisProperties( RiuPlotWidget* plotWidget, - RiuPlotAxis axis, - const RimPlotAxisProperties* const axisProperties, - const QString& axisTitle, - const std::vector& plotCurves ) +void updatePlotWidgetFromAxisProperties( RiuPlotWidget* plotWidget, + RiuPlotAxis axis, + const RimPlotAxisProperties* const axisProperties, + const QString& axisTitle, + const std::vector& plotCurves ) { if ( !plotWidget || !axisProperties ) return; @@ -64,10 +125,12 @@ void RimPlotAxisTools::updatePlotWidgetFromAxisProperties( RiuPlotWidget* plotWidget->setAxisFontsAndAlignment( axis, axisProperties->titleFontSize(), axisProperties->valuesFontSize(), false, alignment ); if ( !axisTitle.isEmpty() ) { - plotWidget->setAxisTitleText( axis, axisTitle ); + plotWidget->setAxisTitleText( axis, axisTitle + RimPlotAxisTools::scaleFactorText( axisProperties ) ); } plotWidget->setAxisTitleEnabled( axis, true ); + applyAxisScaleDraw( plotWidget, axis, axisProperties ); + if ( axisProperties->isLogarithmicScaleEnabled() ) { bool isLogScale = plotWidget->axisScaleType( axis ) == RiuPlotWidget::AxisScaleType::LOGARITHMIC; @@ -123,3 +186,43 @@ void RimPlotAxisTools::updatePlotWidgetFromAxisProperties( RiuPlotWidget* plotWidget->enableAxis( axis, false ); } } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void applyAxisScaleDraw( RiuPlotWidget* plotWidget, RiuPlotAxis axis, const RimPlotAxisProperties* const axisProperties ) +{ + if ( auto qwtPlotWidget = dynamic_cast( plotWidget ) ) + { + auto qwtAxisId = qwtPlotWidget->toQwtPlotAxis( axis ); + + if ( axisProperties->numberFormat() == RimPlotAxisProperties::NUMBER_FORMAT_AUTO && axisProperties->scaleFactor() == 1.0 ) + { + // Default to Qwt's own scale draw to avoid changing too much for default values + qwtPlotWidget->qwtPlot()->setAxisScaleDraw( qwtAxisId, new QwtScaleDraw ); + } + else + { + qwtPlotWidget->qwtPlot()->setAxisScaleDraw( qwtAxisId, + new SummaryScaleDraw( axisProperties->scaleFactor(), + axisProperties->decimalCount(), + axisProperties->numberFormat() ) ); + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString scaleFactorText( const RimPlotAxisProperties* const axisProperties ) +{ + if ( axisProperties->scaleFactor() != 1.0 ) + { + int exponent = std::log10( axisProperties->scaleFactor() ); + return QString( " x 10%1 " ).arg( QString::number( exponent ) ); + } + + return {}; +} + +} // namespace RimPlotAxisTools diff --git a/ApplicationLibCode/ProjectDataModel/Tools/RimPlotAxisTools.h b/ApplicationLibCode/ProjectDataModel/Tools/RimPlotAxisTools.h index 013283e738..227712b5ee 100644 --- a/ApplicationLibCode/ProjectDataModel/Tools/RimPlotAxisTools.h +++ b/ApplicationLibCode/ProjectDataModel/Tools/RimPlotAxisTools.h @@ -36,4 +36,7 @@ void updatePlotWidgetFromAxisProperties( RiuPlotWidget* const QString& axisTitle, const std::vector& plotCurves ); +void applyAxisScaleDraw( RiuPlotWidget* plotWidget, RiuPlotAxis axis, const RimPlotAxisProperties* const axisProperties ); +QString scaleFactorText( const RimPlotAxisProperties* const axisProperties ); + }; // namespace RimPlotAxisTools diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimCustomVfpPlot.cpp b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimCustomVfpPlot.cpp index a1879b976a..7a096476ba 100644 --- a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimCustomVfpPlot.cpp +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimCustomVfpPlot.cpp @@ -21,6 +21,9 @@ #include "RiaColorTables.h" #include "RiaColorTools.h" #include "RiaEclipseUnitTools.h" +#include "RiaPreferences.h" + +#include "RifCsvDataTableFormatter.h" #include "RigVfpTables.h" @@ -71,7 +74,7 @@ RimCustomVfpPlot::RimCustomVfpPlot() CAF_PDM_InitFieldNoDefault( &m_additionalDataSources, "AdditionalDataSources", "Additional Data Sources" ); m_additionalDataSources.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() ); - CAF_PDM_InitFieldNoDefault( &m_curveOptionFiltering, "CurveOptionFiltering", "Curve Option Filtering" ); + CAF_PDM_InitFieldNoDefault( &m_curveValueOptions, "CurveValueOptions", "Curve Value Options" ); CAF_PDM_InitFieldNoDefault( &m_curveMatchingType, "CurveMatchingType", "Curve Matching Type" ); caf::AppEnum defaultTableType = RimVfpDefines::TableType::INJECTION; @@ -103,33 +106,35 @@ RimCustomVfpPlot::RimCustomVfpPlot() caf::AppEnum defaultFamilyVariable = RimVfpDefines::ProductionVariableType::THP; CAF_PDM_InitField( &m_familyVariable, "FamilyVariable", defaultFamilyVariable, "Family Variable" ); - CAF_PDM_InitField( &m_flowRateIdx, "LiquidFlowRateIdx", { 0 }, "Flow Rate" ); - m_flowRateIdx.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() ); + CAF_PDM_InitFieldNoDefault( &m_flowRate, "LiquidFlowRate", "Flow Rate" ); + m_flowRate.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() ); - CAF_PDM_InitField( &m_thpIdx, "THPIdx", { 0 }, "THP" ); - m_thpIdx.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() ); + CAF_PDM_InitFieldNoDefault( &m_thp, "THP", "THP" ); + m_thp.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() ); - CAF_PDM_InitField( &m_articifialLiftQuantityIdx, "ArtificialLiftQuantityIdx", { 0 }, "Artificial Lift Quantity" ); - m_articifialLiftQuantityIdx.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() ); + CAF_PDM_InitFieldNoDefault( &m_artificialLiftQuantity, "ArtificialLiftQuantity", "Artificial Lift Quantity" ); + m_artificialLiftQuantity.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() ); - CAF_PDM_InitField( &m_waterCutIdx, "WaterCutIdx", { 0 }, "Water Cut" ); - m_waterCutIdx.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() ); + CAF_PDM_InitFieldNoDefault( &m_waterCut, "WaterCut", "Water Cut" ); + m_waterCut.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() ); - CAF_PDM_InitField( &m_gasLiquidRatioIdx, "GasLiquidRatioIdx", { 0 }, "Gas Liquid Ratio" ); - m_gasLiquidRatioIdx.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() ); - - CAF_PDM_InitField( &m_familyValues, "FamilyValues", { 0 }, "Family Values" ); - m_familyValues.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() ); + CAF_PDM_InitFieldNoDefault( &m_gasLiquidRatio, "GasLiquidRatio", "Gas Liquid Ratio" ); + m_gasLiquidRatio.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() ); CAF_PDM_InitFieldNoDefault( &m_xAxisProperties, "xAxisProperties", "X Axis" ); m_xAxisProperties = new RimPlotAxisProperties; m_xAxisProperties->setNameAndAxis( "X-Axis", "X-Axis", RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM ); - m_xAxisProperties->setEnableTitleTextSettings( false ); + m_xAxisProperties->configureForBasicUse(); CAF_PDM_InitFieldNoDefault( &m_yAxisProperties, "yAxisProperties", "Y Axis" ); m_yAxisProperties = new RimPlotAxisProperties; m_yAxisProperties->setNameAndAxis( "Y-Axis", "Y-Axis", RiaDefines::PlotAxis::PLOT_AXIS_LEFT ); - m_yAxisProperties->setEnableTitleTextSettings( false ); + m_yAxisProperties->configureForBasicUse(); + + CAF_PDM_InitField( &m_curveThickness, "CurveThickness", 2, "Line Thickness" ); + m_curveThickness.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() ); + + CAF_PDM_InitField( &m_curveSymbolSize, "CurveSymbolSize", 10, "Symbol Size" ); connectAxisSignals( m_xAxisProperties() ); connectAxisSignals( m_yAxisProperties() ); @@ -184,9 +189,29 @@ void RimCustomVfpPlot::initializeObject() auto table = vfpTables->getTableInitialData( tableNumber ); initializeFromInitData( table ); +} - auto values = vfpTables->getProductionTableData( m_tableNumber(), m_familyVariable() ); - m_familyValues = values; +//-------------------------------------------------------------------------------------------------- +/// +void RimCustomVfpPlot::initializeSelection() +{ + std::map>*> typeAndField = + { { RimVfpDefines::ProductionVariableType::FLOW_RATE, &m_flowRate }, + { RimVfpDefines::ProductionVariableType::THP, &m_thp }, + { RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY, &m_artificialLiftQuantity }, + { RimVfpDefines::ProductionVariableType::WATER_CUT, &m_waterCut }, + { RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO, &m_gasLiquidRatio } }; + + for ( const auto& [variableType, field] : typeAndField ) + { + auto values = availableValues( variableType ); + if ( m_familyVariable() == variableType ) + field->v() = values; + else if ( !values.empty() ) + field->v() = { values.front() }; + else + field->v() = {}; + } } //-------------------------------------------------------------------------------------------------- @@ -228,9 +253,8 @@ void RimCustomVfpPlot::updateAxes() { if ( !m_plotWidget ) return; - QString title; - RimPlotAxisTools::updatePlotWidgetFromAxisProperties( m_plotWidget, RiuPlotAxis::defaultBottom(), m_xAxisProperties(), title, {} ); - RimPlotAxisTools::updatePlotWidgetFromAxisProperties( m_plotWidget, RiuPlotAxis::defaultLeft(), m_yAxisProperties(), title, {} ); + RimPlotAxisTools::updatePlotWidgetFromAxisProperties( m_plotWidget, RiuPlotAxis::defaultBottom(), m_xAxisProperties(), m_xAxisTitle, {} ); + RimPlotAxisTools::updatePlotWidgetFromAxisProperties( m_plotWidget, RiuPlotAxis::defaultLeft(), m_yAxisProperties(), m_yAxisTitle, {} ); m_plotWidget->scheduleReplot(); } @@ -267,7 +291,47 @@ void RimCustomVfpPlot::updateLegend() //-------------------------------------------------------------------------------------------------- QString RimCustomVfpPlot::asciiDataForPlotExport() const { - return {}; + QString asciiData; + + size_t plotCurveIdx = 0; + + for ( const auto& curveData : m_plotData ) + { + for ( size_t curveIdx = 0; curveIdx < curveData.size(); curveIdx++ ) + { + asciiData += curveData.curveTitle( curveIdx ); + + if ( !m_additionalDataSources.empty() && plotCurveIdx < m_plotCurveMetaData.size() ) + { + auto plotCurveData = m_plotCurveMetaData[plotCurveIdx]; + asciiData += QString( " (Table: %1)" ).arg( plotCurveData.tableNumber ); + } + + asciiData += "\n"; + + QTextStream stream( &asciiData ); + RifCsvDataTableFormatter formatter( stream, RiaPreferences::current()->csvTextExportFieldSeparator ); + + std::vector header; + const int precision = 2; + header.emplace_back( curveData.xAxisTitle(), RifTextDataTableDoubleFormatting( RIF_FLOAT, precision ) ); + header.emplace_back( curveData.yAxisTitle(), RifTextDataTableDoubleFormatting( RIF_FLOAT, precision ) ); + formatter.header( header ); + + for ( size_t i = 0; i < curveData.xData( curveIdx ).size(); i++ ) + { + formatter.add( curveData.xData( curveIdx )[i] ); + formatter.add( curveData.yData( curveIdx )[i] ); + formatter.rowCompleted(); + } + formatter.tableCompleted(); + + plotCurveIdx++; + } + asciiData += "\n"; + } + + return asciiData; } //-------------------------------------------------------------------------------------------------- @@ -275,7 +339,7 @@ QString RimCustomVfpPlot::asciiDataForPlotExport() const //-------------------------------------------------------------------------------------------------- void RimCustomVfpPlot::reattachAllCurves() { - for ( auto curve : m_plotCurves() ) + for ( const auto& curve : m_plotCurves() ) { if ( curve->isChecked() ) { @@ -289,7 +353,7 @@ void RimCustomVfpPlot::reattachAllCurves() //-------------------------------------------------------------------------------------------------- void RimCustomVfpPlot::detachAllCurves() { - for ( auto curve : m_plotCurves() ) + for ( const auto& curve : m_plotCurves() ) { curve->detach(); } @@ -308,35 +372,43 @@ QString RimCustomVfpPlot::description() const //-------------------------------------------------------------------------------------------------- QString RimCustomVfpPlot::infoForCurve( RimPlotCurve* plotCurve ) const { - std::vector tables = m_additionalDataSources.ptrReferencedObjectsByType(); - tables.push_back( m_mainDataSource ); + auto plotCurveIdx = m_plotCurves.indexOf( plotCurve ); - auto index = m_plotCurves.indexOf( plotCurve ); - - size_t curveCount = 0; - for ( size_t i = 0; i < m_plotData.size(); i++ ) + if ( plotCurveIdx < m_plotCurveMetaData.size() ) { - curveCount += m_plotData[i].size(); - if ( index < curveCount ) - { - auto tableIndex = i; + auto values = m_plotCurveMetaData[plotCurveIdx]; + QString info = QString( "Table: %1\n" ).arg( values.tableNumber ); + info += values.curveName; - auto table = tables[tableIndex]; - QString info = QString( "Table: %1" ).arg( table->tableNumber() ); + if ( m_familyVariable() != RimVfpDefines::ProductionVariableType::WATER_CUT && m_waterCut().size() > 1 ) + info += QString( "\nWC: %1 %2" ) + .arg( convertToDisplayUnit( values.waterCutValue, RimVfpDefines::ProductionVariableType::WATER_CUT ) ) + .arg( getDisplayUnitWithBracket( RimVfpDefines::ProductionVariableType::WATER_CUT ) ); - auto curveIndex = index - ( curveCount - m_plotData[i].size() ); - auto selection = tableSelection( table ); - if ( curveIndex < selection.familyValues.size() ) - { - auto displayValue = convertToDisplayUnit( selection.familyValues[curveIndex], m_familyVariable() ); - auto unitText = getDisplayUnit( m_familyVariable() ); - auto variableName = m_familyVariable().uiText(); + if ( m_familyVariable() != RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO && m_gasLiquidRatio().size() > 1 ) + info += QString( "\nGLR: %1 %2" ) + .arg( convertToDisplayUnit( values.gasLiquidRatioValue, RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO ) ) + .arg( getDisplayUnitWithBracket( RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO ) ); - info += QString( " - %1 %2 %3 " ).arg( variableName ).arg( displayValue ).arg( unitText ); - } + if ( m_familyVariable() != RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY && m_artificialLiftQuantity().size() > 1 ) + info += QString( "\nLift: %1 %2" ) + .arg( convertToDisplayUnit( values.artificialLiftQuantityValue, + RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY ) ) + .arg( getDisplayUnitWithBracket( RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY ) ); - return info; - } + if ( m_familyVariable() != RimVfpDefines::ProductionVariableType::THP && m_thp().size() > 1 ) + info += QString( "\nTPH: %1 %2" ) + .arg( convertToDisplayUnit( values.thpValue, RimVfpDefines::ProductionVariableType::THP ) ) + .arg( getDisplayUnitWithBracket( RimVfpDefines::ProductionVariableType::THP ) ); + + if ( m_familyVariable() != RimVfpDefines::ProductionVariableType::FLOW_RATE && m_flowRate().size() > 1 ) + info += QString( "\nRate: %1 %2" ) + .arg( convertToDisplayUnit( values.flowRateValue, RimVfpDefines::ProductionVariableType::FLOW_RATE ) ) + .arg( getDisplayUnitWithBracket( RimVfpDefines::ProductionVariableType::FLOW_RATE ) ); + + info += "\n"; + + return info; } return {}; @@ -470,6 +542,27 @@ void RimCustomVfpPlot::deleteViewWidget() } } +//-------------------------------------------------------------------------------------------------- +/// Create all possible combinations of the vectors passed in. +//-------------------------------------------------------------------------------------------------- +void generateCombinations( const std::vector>& vectors, + std::vector& currentCombination, + std::vector>& allCombinations, + size_t depth ) +{ + if ( depth == vectors.size() ) + { + allCombinations.push_back( currentCombination ); + return; + } + + for ( const auto& value : vectors[depth] ) + { + currentCombination[depth] = value; + generateCombinations( vectors, currentCombination, allCombinations, depth + 1 ); + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -500,26 +593,86 @@ void RimCustomVfpPlot::onLoadDataAndUpdate() tables.push_back( m_mainDataSource ); m_plotData.clear(); + m_plotCurveMetaData.clear(); for ( const auto& table : tables ) { if ( !table ) continue; - int tableIndex = table->tableNumber(); - auto vfpTables = table->dataSource()->vfpTables(); + int tableNumber = table->tableNumber(); + auto vfpTables = table->dataSource()->vfpTables(); - auto vfpPlotData = vfpTables->populatePlotData( tableIndex, - m_primaryVariable(), - m_familyVariable(), - m_interpolatedVariable(), - m_flowingPhase(), - tableSelection( table ) ); + if ( table->tableType() == RimVfpDefines::TableType::INJECTION ) + { + auto vfpPlotData = vfpTables->populatePlotData( tableNumber, + m_primaryVariable(), + m_familyVariable(), + m_interpolatedVariable(), + m_flowingPhase(), + VfpTableSelection() ); - m_plotData.push_back( vfpPlotData ); + m_plotData.push_back( vfpPlotData ); - QColor curveColor = RiaColorTables::summaryCurveDefaultPaletteColors().cycledQColor( colorIndex++ ); + QColor curveColor = curveColors().cycledQColor( colorIndex ); - populatePlotWidgetWithPlotData( m_plotWidget, vfpPlotData, curveColor ); + auto symbols = curveSymbols(); + auto symbol = symbols[colorIndex % symbols.size()]; + + bool multipleCurveSets = tables.size() > 1; + CurveNameContent curveNameContent; + curveNameContent.defaultName = true; + populatePlotWidgetWithPlotData( m_plotWidget, + vfpPlotData, + VfpValueSelection(), + tableNumber, + curveColor, + symbol, + multipleCurveSets, + curveNameContent ); + colorIndex++; + } + else + { + auto valueSelections = computeValueSelectionCombinations(); + for ( auto& valueSelection : valueSelections ) + { + valueSelection.familyValues = familyValuesForTable( table ); + + auto vfpPlotData = vfpTables->populatePlotData( tableNumber, + m_primaryVariable(), + m_familyVariable(), + m_interpolatedVariable(), + m_flowingPhase(), + valueSelection ); + + m_plotData.push_back( vfpPlotData ); + + QColor curveColor = curveColors().cycledQColor( colorIndex ); + + auto symbols = curveSymbols(); + auto symbol = symbols[colorIndex % symbols.size()]; + + bool multipleCurveSets = ( tables.size() > 1 || ( valueSelections.size() > 1 ) ); + + CurveNameContent curveNameContent; + if ( tables.size() > 1 ) curveNameContent.tableNumber = true; + if ( m_flowRate().size() > 1 ) curveNameContent.flowRate = true; + if ( m_thp().size() > 1 ) curveNameContent.thp = true; + if ( m_artificialLiftQuantity().size() > 1 ) curveNameContent.artificialLiftQuantity = true; + if ( m_waterCut().size() > 1 ) curveNameContent.waterCut = true; + if ( m_gasLiquidRatio().size() > 1 ) curveNameContent.gasLiquidRatio = true; + + populatePlotWidgetWithPlotData( m_plotWidget, + vfpPlotData, + valueSelection, + tableNumber, + curveColor, + symbol, + multipleCurveSets, + curveNameContent ); + colorIndex++; + } + } } updatePlotTitle( @@ -538,36 +691,108 @@ void RimCustomVfpPlot::onLoadDataAndUpdate() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimCustomVfpPlot::populatePlotWidgetWithPlotData( RiuPlotWidget* plotWidget, const VfpPlotData& plotData, const QColor& color ) +void RimCustomVfpPlot::populatePlotWidgetWithPlotData( RiuPlotWidget* plotWidget, + const VfpPlotData& plotData, + const VfpValueSelection& valueSelection, + int tableNumber, + const QColor& color, + RiuPlotCurveSymbol::PointSymbolEnum curveSymbol, + bool multipleCurveSets, + const CurveNameContent& curveNameContent ) { + if ( !plotWidget ) return; + plotWidget->setAxisScale( RiuPlotAxis::defaultBottom(), 0, 1 ); plotWidget->setAxisScale( RiuPlotAxis::defaultLeft(), 0, 1 ); plotWidget->setAxisAutoScale( RiuPlotAxis::defaultBottom(), true ); plotWidget->setAxisAutoScale( RiuPlotAxis::defaultLeft(), true ); - plotWidget->setAxisTitleText( RiuPlotAxis::defaultBottom(), plotData.xAxisTitle() ); - plotWidget->setAxisTitleText( RiuPlotAxis::defaultLeft(), plotData.yAxisTitle() ); - for ( auto idx = 0u; idx < plotData.size(); idx++ ) + m_xAxisTitle = plotData.xAxisTitle(); + m_yAxisTitle = plotData.yAxisTitle(); + + auto formatCurveNamePart = + [&]( RimVfpDefines::ProductionVariableType variableType, double familyValue, double selectionValue, const QString& namePart ) -> QString + { + double value = ( variableType == m_familyVariable() ) ? familyValue : selectionValue; + double displayValue = convertToDisplayUnit( value, variableType ); + return QString( " %1:%2" ).arg( namePart ).arg( displayValue ); + }; + + for ( auto curveIndex = 0u; curveIndex < plotData.size(); curveIndex++ ) { auto curve = new RimPlotCurve(); curve->setLineStyle( RiuQwtPlotCurveDefines::LineStyleEnum::STYLE_SOLID ); - curve->setLineThickness( 2 ); - curve->setColor( RiaColorTools::fromQColorTo3f( color ) ); - curve->setSymbol( RiuPlotCurveSymbol::SYMBOL_ELLIPSE ); - curve->setSymbolSize( 6 ); + curve->setLineThickness( m_curveThickness() ); - curve->setCustomName( plotData.curveTitle( idx ) ); + if ( multipleCurveSets ) + { + // Use the incoming color for all curves, and cycle the symbols + curve->setColor( RiaColorTools::fromQColorTo3f( color ) ); + auto symbols = curveSymbols(); + auto customSymbol = symbols[curveIndex % symbols.size()]; + curve->setSymbol( customSymbol ); + } + else + { + // Use the incoming symbol for all curves, and cycle the colors + auto customColor = curveColors().cycledQColor( curveIndex ); + curve->setColor( RiaColorTools::fromQColorTo3f( customColor ) ); + curve->setSymbol( curveSymbol ); + } + curve->setSymbolSize( m_curveSymbolSize() ); + + QString curveName; + if ( curveNameContent.defaultName ) curveName = plotData.curveTitle( curveIndex ); + if ( curveNameContent.tableNumber ) curveName += QString( " Table:%1" ).arg( tableNumber ); + + auto familyValue = ( curveIndex < valueSelection.familyValues.size() ) ? valueSelection.familyValues[curveIndex] : 0.0; + + using pvt = RimVfpDefines::ProductionVariableType; + + if ( curveNameContent.thp || m_familyVariable() == pvt::THP ) + { + curveName += formatCurveNamePart( pvt::THP, familyValue, valueSelection.thpValue, "THP" ); + } + if ( curveNameContent.gasLiquidRatio || m_familyVariable() == pvt::GAS_LIQUID_RATIO ) + { + curveName += formatCurveNamePart( pvt::GAS_LIQUID_RATIO, familyValue, valueSelection.gasLiquidRatioValue, "GLR" ); + } + if ( curveNameContent.waterCut || m_familyVariable() == pvt::WATER_CUT ) + { + curveName += formatCurveNamePart( pvt::WATER_CUT, familyValue, valueSelection.waterCutValue, "WC" ); + } + if ( curveNameContent.artificialLiftQuantity || m_familyVariable() == pvt::ARTIFICIAL_LIFT_QUANTITY ) + { + curveName += formatCurveNamePart( pvt::ARTIFICIAL_LIFT_QUANTITY, familyValue, valueSelection.artificialLiftQuantityValue, "Lift" ); + } + if ( curveNameContent.flowRate || m_familyVariable() == pvt::FLOW_RATE ) + { + curveName += formatCurveNamePart( pvt::FLOW_RATE, familyValue, valueSelection.flowRateValue, "Rate" ); + } + + curve->setCustomName( curveName.trimmed() ); curve->setParentPlotNoReplot( plotWidget ); if ( curve->plotCurve() ) { bool useLogarithmicScale = false; - curve->plotCurve()->setSamplesFromXValuesAndYValues( plotData.xData( idx ), plotData.yData( idx ), useLogarithmicScale ); + curve->plotCurve()->setSamplesFromXValuesAndYValues( plotData.xData( curveIndex ), plotData.yData( curveIndex ), useLogarithmicScale ); } curve->updateCurveAppearance(); curve->appearanceChanged.connect( this, &RimCustomVfpPlot::curveAppearanceChanged ); m_plotCurves.push_back( curve ); + + PlotCurveData plotCurveData; + plotCurveData.curveName = plotData.curveTitle( curveIndex ); + plotCurveData.tableNumber = tableNumber; + plotCurveData.flowRateValue = valueSelection.flowRateValue; + plotCurveData.thpValue = valueSelection.thpValue; + plotCurveData.artificialLiftQuantityValue = valueSelection.artificialLiftQuantityValue; + plotCurveData.waterCutValue = valueSelection.waterCutValue; + plotCurveData.gasLiquidRatioValue = valueSelection.gasLiquidRatioValue; + + m_plotCurveMetaData.emplace_back( plotCurveData ); } updateConnectedEditors(); @@ -674,7 +899,7 @@ std::vector RimCustomVfpPlot::availableValues( RimVfpDefines::Production auto values = m_mainDataSource->dataSource()->vfpTables()->getProductionTableData( m_tableNumber(), variableType ); - if ( m_curveOptionFiltering() == RimVfpDefines::CurveOptionValuesType::UNION_OF_SELECTED_TABLES ) + if ( m_curveValueOptions() == RimVfpDefines::CurveOptionValuesType::UNION_OF_SELECTED_TABLES ) { std::vector tables = m_additionalDataSources.ptrReferencedObjectsByType(); for ( const auto& table : tables ) @@ -701,6 +926,107 @@ RiuPlotCurveInfoTextProvider* RimCustomVfpPlot::curveTextProvider() return &textProvider; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimCustomVfpPlot::nonFamilyProductionVariables() const +{ + std::vector variables; + + auto allVariables = { RimVfpDefines::ProductionVariableType::FLOW_RATE, + RimVfpDefines::ProductionVariableType::THP, + RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY, + RimVfpDefines::ProductionVariableType::WATER_CUT, + RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO }; + + for ( const auto& variable : allVariables ) + { + if ( variable != m_familyVariable() ) + { + variables.push_back( variable ); + } + } + return variables; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimCustomVfpPlot::computeValueSelectionCombinations() const +{ + auto populateValueSelection = []( VfpValueSelection& selection, RimVfpDefines::ProductionVariableType variableType, double value ) + { + switch ( variableType ) + { + case RimVfpDefines::ProductionVariableType::FLOW_RATE: + selection.flowRateValue = value; + break; + case RimVfpDefines::ProductionVariableType::THP: + selection.thpValue = value; + break; + case RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY: + selection.artificialLiftQuantityValue = value; + break; + case RimVfpDefines::ProductionVariableType::WATER_CUT: + selection.waterCutValue = value; + break; + case RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO: + selection.gasLiquidRatioValue = value; + break; + } + }; + + auto availableVariables = nonFamilyProductionVariables(); + + std::vector> variableVectors; + for ( auto variableType : availableVariables ) + { + variableVectors.push_back( valuesForProductionType( variableType ) ); + } + + std::vector> allCombinations; + std::vector currentCombination( variableVectors.size() ); + generateCombinations( variableVectors, currentCombination, allCombinations, 0 ); + + std::vector valueSelections; + for ( const auto& combination : allCombinations ) + { + VfpValueSelection selection; + for ( size_t i = 0; i < availableVariables.size(); ++i ) + { + populateValueSelection( selection, availableVariables[i], combination[i] ); + } + + valueSelections.push_back( selection ); + } + + return valueSelections; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimCustomVfpPlot::curveSymbols() +{ + return { + RiuPlotCurveSymbol::SYMBOL_ELLIPSE, + RiuPlotCurveSymbol::SYMBOL_CROSS, + RiuPlotCurveSymbol::SYMBOL_DIAMOND, + RiuPlotCurveSymbol::SYMBOL_XCROSS, + RiuPlotCurveSymbol::SYMBOL_LEFT_TRIANGLE, + RiuPlotCurveSymbol::SYMBOL_STAR1, + RiuPlotCurveSymbol::SYMBOL_RIGHT_TRIANGLE, + }; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const caf::ColorTable RimCustomVfpPlot::curveColors() +{ + return RiaColorTables::summaryCurveDefaultPaletteColors().inverted(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -772,54 +1098,59 @@ QString RimCustomVfpPlot::getDisplayUnit( RimVfpDefines::ProductionVariableType void RimCustomVfpPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { uiOrdering.add( &m_mainDataSource ); - uiOrdering.add( &m_additionalDataSources ); - uiOrdering.add( &m_curveMatchingType ); - uiOrdering.add( &m_curveOptionFiltering ); + { + auto group = uiOrdering.addNewGroup( "Configuration" ); + group->add( &m_curveMatchingType ); + group->add( &m_curveValueOptions ); + group->add( &m_interpolatedVariable ); + if ( m_tableType == RimVfpDefines::TableType::PRODUCTION ) + { + group->add( &m_primaryVariable ); + group->add( &m_familyVariable ); + } + } - uiOrdering.add( &m_tableType ); - uiOrdering.add( &m_tableNumber ); - uiOrdering.add( &m_referenceDepth ); - uiOrdering.add( &m_interpolatedVariable ); - uiOrdering.add( &m_flowingPhase ); + { + auto group = uiOrdering.addNewGroup( "Table Details" ); + group->add( &m_tableType ); + group->add( &m_tableNumber ); + group->add( &m_referenceDepth ); + group->add( &m_flowingPhase ); + + if ( m_tableType == RimVfpDefines::TableType::PRODUCTION ) + { + group->add( &m_flowingWaterFraction ); + group->add( &m_flowingGasFraction ); + } + } + + { + auto group = uiOrdering.addNewGroup( "Additional Tables" ); + group->setCollapsedByDefault(); + group->add( &m_additionalDataSources ); + } if ( m_tableType == RimVfpDefines::TableType::PRODUCTION ) { - uiOrdering.add( &m_flowingWaterFraction ); - uiOrdering.add( &m_flowingGasFraction ); + auto selectionDetailsGroup = uiOrdering.addNewGroup( "Selection Details" ); + selectionDetailsGroup->setCollapsedByDefault(); + selectionDetailsGroup->add( &m_flowRate ); + selectionDetailsGroup->add( &m_thp ); + selectionDetailsGroup->add( &m_artificialLiftQuantity ); + selectionDetailsGroup->add( &m_waterCut ); + selectionDetailsGroup->add( &m_gasLiquidRatio ); + } - uiOrdering.add( &m_primaryVariable ); - uiOrdering.add( &m_familyVariable ); - - caf::PdmUiOrdering* fixedVariablesGroup = uiOrdering.addNewGroup( "Fixed Variables" ); - fixedVariablesGroup->add( &m_flowRateIdx ); - fixedVariablesGroup->add( &m_thpIdx ); - fixedVariablesGroup->add( &m_articifialLiftQuantityIdx ); - fixedVariablesGroup->add( &m_waterCutIdx ); - fixedVariablesGroup->add( &m_gasLiquidRatioIdx ); - - fixedVariablesGroup->add( &m_familyValues ); - - // Disable the choices for variables as primary or family - setFixedVariableUiEditability( m_flowRateIdx, RimVfpDefines::ProductionVariableType::FLOW_RATE ); - setFixedVariableUiEditability( m_thpIdx, RimVfpDefines::ProductionVariableType::THP ); - setFixedVariableUiEditability( m_articifialLiftQuantityIdx, RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY ); - setFixedVariableUiEditability( m_waterCutIdx, RimVfpDefines::ProductionVariableType::WATER_CUT ); - setFixedVariableUiEditability( m_gasLiquidRatioIdx, RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO ); + { + auto group = uiOrdering.addNewGroup( "Appearance" ); + group->add( &m_curveThickness ); + group->add( &m_curveSymbolSize ); } uiOrdering.skipRemainingFields( true ); } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimCustomVfpPlot::setFixedVariableUiEditability( caf::PdmFieldHandle& field, RimVfpDefines::ProductionVariableType variableType ) -{ - field.uiCapability()->setUiReadOnly( variableType == m_primaryVariable.v() ); - field.uiCapability()->setUiHidden( variableType == m_familyVariable.v() ); -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -827,36 +1158,31 @@ QList RimCustomVfpPlot::calculateValueOptions( const caf { QList options = RimPlot::calculateValueOptions( fieldNeedingOptions ); - if ( fieldNeedingOptions == &m_flowRateIdx ) + if ( fieldNeedingOptions == &m_flowRate ) { calculateTableValueOptions( RimVfpDefines::ProductionVariableType::FLOW_RATE, options ); } - else if ( fieldNeedingOptions == &m_thpIdx ) + else if ( fieldNeedingOptions == &m_thp ) { calculateTableValueOptions( RimVfpDefines::ProductionVariableType::THP, options ); } - else if ( fieldNeedingOptions == &m_articifialLiftQuantityIdx ) + else if ( fieldNeedingOptions == &m_artificialLiftQuantity ) { calculateTableValueOptions( RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY, options ); } - else if ( fieldNeedingOptions == &m_waterCutIdx ) + else if ( fieldNeedingOptions == &m_waterCut ) { calculateTableValueOptions( RimVfpDefines::ProductionVariableType::WATER_CUT, options ); } - else if ( fieldNeedingOptions == &m_gasLiquidRatioIdx ) + else if ( fieldNeedingOptions == &m_gasLiquidRatio ) { calculateTableValueOptions( RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO, options ); } - else if ( fieldNeedingOptions == &m_familyValues ) - { - calculateTableValueOptions( m_familyVariable(), options ); - } - else if ( fieldNeedingOptions == &m_additionalDataSources ) { RimVfpDataCollection* vfpDataCollection = RimVfpDataCollection::instance(); @@ -878,9 +1204,60 @@ QList RimCustomVfpPlot::calculateValueOptions( const caf } } + else if ( fieldNeedingOptions == &m_curveThickness ) + { + for ( size_t i = 0; i < 10; i++ ) + { + options.push_back( caf::PdmOptionItemInfo( QString::number( i + 1 ), QVariant::fromValue( i + 1 ) ) ); + } + } + return options; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimCustomVfpPlot::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) +{ + if ( auto attrib = dynamic_cast( attribute ) ) + { + attrib->showTextFilter = false; + } + + if ( field == &m_mainDataSource ) + { + if ( auto* myAttr = dynamic_cast( attribute ) ) + { + myAttr->showPreviousAndNextButtons = true; + myAttr->nextIcon = QIcon( ":/ComboBoxDown.svg" ); + myAttr->previousIcon = QIcon( ":/ComboBoxUp.svg" ); + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimCustomVfpPlot::valuesForProductionType( RimVfpDefines::ProductionVariableType variableType ) const +{ + switch ( variableType ) + { + case RimVfpDefines::ProductionVariableType::FLOW_RATE: + return m_flowRate(); + case RimVfpDefines::ProductionVariableType::THP: + return m_thp(); + case RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY: + return m_artificialLiftQuantity(); + case RimVfpDefines::ProductionVariableType::WATER_CUT: + return m_waterCut(); + case RimVfpDefines::ProductionVariableType::GAS_LIQUID_RATIO: + return m_gasLiquidRatio(); + } + + return {}; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -888,11 +1265,11 @@ void RimCustomVfpPlot::calculateTableValueOptions( RimVfpDefines::ProductionVari { auto values = availableValues( variableType ); - for ( size_t i = 0; i < values.size(); i++ ) + for ( double value : values ) { options.push_back( - caf::PdmOptionItemInfo( QString( "%1 %2" ).arg( convertToDisplayUnit( values[i], variableType ) ).arg( getDisplayUnit( variableType ) ), - values[i] ) ); + caf::PdmOptionItemInfo( QString( "%1 %2" ).arg( convertToDisplayUnit( value, variableType ) ).arg( getDisplayUnit( variableType ) ), + value ) ); } } @@ -906,11 +1283,14 @@ void RimCustomVfpPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField if ( changedField == &m_mainDataSource ) { initializeObject(); + initializeSelection(); + zoomAll(); } - if ( changedField == &m_familyVariable || changedField == &m_curveOptionFiltering ) + if ( changedField == &m_additionalDataSources || changedField == &m_curveMatchingType || changedField == &m_curveValueOptions || + changedField == &m_primaryVariable || changedField == &m_familyVariable ) { - m_familyValues.v() = availableValues( m_familyVariable() ); + initializeSelection(); } loadDataAndUpdate(); @@ -918,13 +1298,6 @@ void RimCustomVfpPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField updateConnectedEditors(); } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimCustomVfpPlot::initAfterRead() -{ -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -981,33 +1354,30 @@ void RimCustomVfpPlot::scheduleReplot() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -VfpValueSelection RimCustomVfpPlot::tableSelection( RimVfpTable* table ) const +std::vector RimCustomVfpPlot::familyValuesForTable( RimVfpTable* table ) const { - VfpValueSelection selection; + if ( !table || !m_mainDataSource || !m_mainDataSource->dataSource() || !m_mainDataSource->dataSource()->vfpTables() ) return {}; - selection.articifialLiftQuantityValue = m_articifialLiftQuantityIdx(); - selection.flowRateValue = m_flowRateIdx(); - selection.gasLiquidRatioValue = m_gasLiquidRatioIdx(); - selection.thpValue = m_thpIdx(); - selection.waterCutValue = m_waterCutIdx(); + std::vector mainTableFamilyValues = valuesForProductionType( m_familyVariable() ); if ( m_curveMatchingType() == RimVfpDefines::CurveMatchingType::EXACT ) { - selection.familyValues = m_familyValues(); - } - else if ( m_curveMatchingType() == RimVfpDefines::CurveMatchingType::CLOSEST_MATCH_FAMILY ) - { - auto familyValues = m_familyValues(); - if ( table && table->dataSource() && table->dataSource()->vfpTables() ) - { - auto valuesToMatch = table->dataSource()->vfpTables()->getProductionTableData( table->tableNumber(), m_familyVariable() ); - auto indices = RigVfpTables::uniqueClosestIndices( familyValues, valuesToMatch ); - for ( const auto& i : indices ) - { - selection.familyValues.push_back( valuesToMatch[i] ); - } - } + return mainTableFamilyValues; } - return selection; + if ( m_curveMatchingType() == RimVfpDefines::CurveMatchingType::CLOSEST_MATCH_FAMILY ) + { + auto valuesToMatch = table->dataSource()->vfpTables()->getProductionTableData( table->tableNumber(), m_familyVariable() ); + auto indices = RigVfpTables::uniqueClosestIndices( mainTableFamilyValues, valuesToMatch ); + + std::vector values; + for ( const auto& i : indices ) + { + values.push_back( valuesToMatch[i] ); + } + + return values; + } + + return {}; } diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimCustomVfpPlot.h b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimCustomVfpPlot.h index 2797eaceaf..cefc9fd15e 100644 --- a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimCustomVfpPlot.h +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimCustomVfpPlot.h @@ -21,6 +21,8 @@ #include "RimPlot.h" #include "RimVfpDefines.h" +#include "RiuPlotCurveSymbol.h" + #include "cafPdmPtrArrayField.h" #include "cafPdmPtrField.h" @@ -43,6 +45,11 @@ class VFPInjTable; class VFPProdTable; } // namespace Opm +namespace caf +{ +class ColorTable; +} + //-------------------------------------------------------------------------------------------------- /// Vertical Flow Performance Plot //-------------------------------------------------------------------------------------------------- @@ -57,6 +64,7 @@ public: void selectDataSource( RimVfpTable* mainDataSource, const std::vector& vfpTableData ); void setTableNumber( int tableNumber ); void initializeObject(); + void initializeSelection(); // RimPlot implementations RiuPlotWidget* plotWidget() override; @@ -88,21 +96,19 @@ private: void scheduleReplot(); -private: 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; + void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override; - VfpValueSelection tableSelection( RimVfpTable* table ) const; - void initializeFromInitData( const VfpTableInitialData& table ); + std::vector valuesForProductionType( RimVfpDefines::ProductionVariableType variableType ) const; + std::vector familyValuesForTable( RimVfpTable* table ) const; + void initializeFromInitData( const VfpTableInitialData& table ); RiuPlotWidget* doCreatePlotViewWidget( QWidget* mainWindowParent ) override; void calculateTableValueOptions( RimVfpDefines::ProductionVariableType variableType, QList& options ); - void setFixedVariableUiEditability( caf::PdmFieldHandle& field, RimVfpDefines::ProductionVariableType variableType ); - void updatePlotTitle( const QString& plotTitle ); static QString generatePlotTitle( const QString& wellName, int tableNumber, @@ -116,7 +122,25 @@ private: static QString getDisplayUnit( RimVfpDefines::ProductionVariableType variableType ); static QString getDisplayUnitWithBracket( RimVfpDefines::ProductionVariableType variableType ); - void populatePlotWidgetWithPlotData( RiuPlotWidget* plotWidget, const VfpPlotData& plotData, const QColor& color ); + struct CurveNameContent + { + bool defaultName = false; + bool tableNumber = false; + bool flowRate = false; + bool thp = false; + bool artificialLiftQuantity = false; + bool waterCut = false; + bool gasLiquidRatio = false; + }; + + void populatePlotWidgetWithPlotData( RiuPlotWidget* plotWidget, + const VfpPlotData& plotData, + const VfpValueSelection& valueSelection, + int tableNumber, + const QColor& color, + RiuPlotCurveSymbol::PointSymbolEnum curveSymbol, + bool multipleCurveSets, + const CurveNameContent& curveNameContent ); static QString axisTitle( RimVfpDefines::ProductionVariableType variableType, RimVfpDefines::FlowingPhaseType flowingPhase ); @@ -133,6 +157,24 @@ private: static RiuPlotCurveInfoTextProvider* curveTextProvider(); + std::vector nonFamilyProductionVariables() const; + std::vector computeValueSelectionCombinations() const; + + struct PlotCurveData + { + QString curveName; + int tableNumber; + double flowRateValue; + double thpValue; + double artificialLiftQuantityValue; + double waterCutValue; + double gasLiquidRatioValue; + }; + + static std::vector curveSymbols(); + + static const caf::ColorTable curveColors(); + private: caf::PdmField m_plotTitle; @@ -140,7 +182,7 @@ private: caf::PdmPtrArrayField m_additionalDataSources; caf::PdmField> m_curveMatchingType; - caf::PdmField> m_curveOptionFiltering; + caf::PdmField> m_curveValueOptions; caf::PdmField m_tableNumber; caf::PdmField m_referenceDepth; @@ -153,20 +195,25 @@ private: caf::PdmField> m_primaryVariable; caf::PdmField> m_familyVariable; - caf::PdmField m_flowRateIdx; - caf::PdmField m_thpIdx; - caf::PdmField m_articifialLiftQuantityIdx; - caf::PdmField m_waterCutIdx; - caf::PdmField m_gasLiquidRatioIdx; - - caf::PdmField> m_familyValues; + caf::PdmField> m_flowRate; + caf::PdmField> m_thp; + caf::PdmField> m_artificialLiftQuantity; + caf::PdmField> m_waterCut; + caf::PdmField> m_gasLiquidRatio; caf::PdmChildField m_yAxisProperties; caf::PdmChildField m_xAxisProperties; caf::PdmChildArrayField m_plotCurves; - std::vector m_plotData; + caf::PdmField m_curveSymbolSize; + caf::PdmField m_curveThickness; + + std::vector m_plotData; + std::vector m_plotCurveMetaData; + + QString m_xAxisTitle; + QString m_yAxisTitle; QPointer m_plotWidget; }; diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDefines.cpp b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDefines.cpp index 2814469f64..4329c31556 100644 --- a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDefines.cpp +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpDefines.cpp @@ -85,7 +85,7 @@ void caf::AppEnum::setUp() { addItem( RimVfpDefines::CurveMatchingType::EXACT, "EXACT", "Exact" ); addItem( RimVfpDefines::CurveMatchingType::CLOSEST_MATCH_FAMILY, "CLOSEST_MATCH_FAMILY", "Family Closest Match" ); - setDefault( RimVfpDefines::CurveMatchingType::EXACT ); + setDefault( RimVfpDefines::CurveMatchingType::CLOSEST_MATCH_FAMILY ); } template <> diff --git a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlotCollection.cpp b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlotCollection.cpp index 0aff309d53..8ac3b072c0 100644 --- a/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlotCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/VerticalFlowPerformance/RimVfpPlotCollection.cpp @@ -67,6 +67,7 @@ RimCustomVfpPlot* RimVfpPlotCollection::createAndAppendPlots( RimVfpTable* mainD auto vfpPlot = new RimCustomVfpPlot(); vfpPlot->selectDataSource( mainDataSource, tableData ); vfpPlot->initializeObject(); + vfpPlot->initializeSelection(); m_customVfpPlots.push_back( vfpPlot ); diff --git a/ApplicationLibCode/ReservoirDataModel/RigVfpTables.cpp b/ApplicationLibCode/ReservoirDataModel/RigVfpTables.cpp index a6844108fc..cd917639f1 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigVfpTables.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigVfpTables.cpp @@ -659,7 +659,7 @@ size_t RigVfpTables::getVariableIndexForValue( const Opm::VFPProdTable& } case RimVfpDefines::ProductionVariableType::ARTIFICIAL_LIFT_QUANTITY: { - return findClosestIndexForVariable( targetVariable, valueSelection.articifialLiftQuantityValue, table ); + return findClosestIndexForVariable( targetVariable, valueSelection.artificialLiftQuantityValue, table ); } case RimVfpDefines::ProductionVariableType::FLOW_RATE: { @@ -834,7 +834,7 @@ VfpTableInitialData RigVfpTables::getTableInitialData( int tableIndex ) const auto prodTable = productionTable( tableIndex ); if ( prodTable.has_value() ) { - VfpTableInitialData initialData; + VfpTableInitialData initialData{}; initialData.isProductionTable = true; initialData.tableNumber = prodTable->getTableNum(); initialData.datumDepth = prodTable->getDatumDepth(); @@ -848,7 +848,7 @@ VfpTableInitialData RigVfpTables::getTableInitialData( int tableIndex ) const auto injTable = injectionTable( tableIndex ); if ( injTable.has_value() ) { - VfpTableInitialData initialData; + VfpTableInitialData initialData{}; initialData.isProductionTable = false; initialData.tableNumber = injTable->getTableNum(); initialData.datumDepth = injTable->getDatumDepth(); diff --git a/ApplicationLibCode/ReservoirDataModel/RigVfpTables.h b/ApplicationLibCode/ReservoirDataModel/RigVfpTables.h index 1d8b7ab390..8caa1dd8ea 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigVfpTables.h +++ b/ApplicationLibCode/ReservoirDataModel/RigVfpTables.h @@ -77,7 +77,7 @@ struct VfpValueSelection { double flowRateValue; double thpValue; - double articifialLiftQuantityValue; + double artificialLiftQuantityValue; double waterCutValue; double gasLiquidRatioValue;