///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2023- Equinor ASA // // ResInsight is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RimWellAllocationOverTimePlot.h" #include "RiaColorTools.h" #include "RiaDefines.h" #include "RiaLogging.h" #include "RiaPreferences.h" #include "RiaQDateTimeTools.h" #include "RiaStdStringTools.h" #include "RigAccWellFlowCalculator.h" #include "RigEclipseCaseData.h" #include "RigSimWellData.h" #include "RigSimulationWellCenterLineCalculator.h" #include "RigWellAllocationOverTime.h" #include "RigWellResultPoint.h" #include "RimEclipseCaseTools.h" #include "RimEclipseCellColors.h" #include "RimEclipseResultCase.h" #include "RimEclipseView.h" #include "RimFlowDiagSolution.h" #include "RimSimWellInView.h" #include "RimStackablePlotCurve.h" #include "RimWellAllocationTools.h" #include "RimWellLogFile.h" #include "RimWellPlotTools.h" #include "RiuContextMenuLauncher.h" #include "RiuPlotCurve.h" #include "RiuPlotWidget.h" #include "RiuQwtCurvePointTracker.h" #include "RiuQwtPlotTools.h" #include "RiuQwtPlotWidget.h" #include "cafCmdFeatureMenuBuilder.h" #include "cafFontTools.h" #include "cafPdmUiComboBoxEditor.h" #include "cafPdmUiPushButtonEditor.h" #include "cafPdmUiTreeSelectionEditor.h" #include "qwt_scale_draw.h" CAF_PDM_SOURCE_INIT( RimWellAllocationOverTimePlot, "RimWellAllocationOverTimePlot" ); //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- namespace caf { template <> void AppEnum::setUp() { addItem( RimWellAllocationOverTimePlot::FlowValueType::FLOW_RATE, "FLOW_RATE", "Flow Rates" ); addItem( RimWellAllocationOverTimePlot::FlowValueType::FLOW_RATE_PERCENTAGE, "FLOW_RATE_PERCENTAGE", "Flow Rate Percentage" ); addItem( RimWellAllocationOverTimePlot::FlowValueType::FLOW_VOLUME, "FLOW_VOLUME", "Flow Volumes" ); addItem( RimWellAllocationOverTimePlot::FlowValueType::ACCUMULATED_FLOW_VOLUME, "ACCUMULATED_FLOW_VOLUME", "Accumulated Flow Volumes" ); addItem( RimWellAllocationOverTimePlot::FlowValueType::ACCUMULATED_FLOW_VOLUME_PERCENTAGE, "ACCUMULATED_FLOW_VOLUME_PERCENTAGE", "Accumulated Flow Volume Percentage" ); setDefault( RimWellAllocationOverTimePlot::FlowValueType::FLOW_RATE ); } } // namespace caf //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- namespace caf { template <> void AppEnum::setUp() { addItem( RimWellAllocationOverTimePlot::TimeStepFilterMode::NONE, "NONE", "Show All Time Steps" ); addItem( RimWellAllocationOverTimePlot::TimeStepFilterMode::TIME_STEP_COUNT, "TIME_STEP_COUNT", "Time Step Count" ); setDefault( RimWellAllocationOverTimePlot::TimeStepFilterMode::TIME_STEP_COUNT ); } } // namespace caf //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimWellAllocationOverTimePlot::RimWellAllocationOverTimePlot() { CAF_PDM_InitObject( "Well Allocation Over Time Plot", ":/AnalysisPlot16x16.png" ); CAF_PDM_InitField( &m_userName, "PlotDescription", QString( "Well Allocation Over Time Plot" ), "Name" ); m_userName.uiCapability()->setUiReadOnly( true ); CAF_PDM_InitFieldNoDefault( &m_case, "CurveCase", "Case" ); m_case.uiCapability()->setUiTreeChildrenHidden( true ); CAF_PDM_InitField( &m_wellName, "WellName", QString( "None" ), "Well" ); CAF_PDM_InitFieldNoDefault( &m_selectedFromTimeStep, "FromTimeStep", "From Time Step" ); m_selectedFromTimeStep.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() ); CAF_PDM_InitFieldNoDefault( &m_selectedToTimeStep, "ToTimeStep", "To Time Step" ); m_selectedToTimeStep.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() ); CAF_PDM_InitFieldNoDefault( &m_timeStepFilterMode, "TimeStepRangeFilterMode", "Filter" ); m_timeStepFilterMode.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() ); CAF_PDM_InitField( &m_timeStepCount, "TimeStepCount", m_initialNumberOfTimeSteps, "Number of Time Steps" ); CAF_PDM_InitFieldNoDefault( &m_excludeTimeSteps, "ExcludeTimeSteps", "" ); m_excludeTimeSteps.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() ); CAF_PDM_InitFieldNoDefault( &m_applyTimeStepSelections, "ApplyTimeStepSelections", "" ); caf::PdmUiPushButtonEditor::configureEditorForField( &m_applyTimeStepSelections ); CAF_PDM_InitFieldNoDefault( &m_flowDiagSolution, "FlowDiagSolution", "Plot Type" ); CAF_PDM_InitFieldNoDefault( &m_flowValueType, "FlowValueType", "Value Type" ); CAF_PDM_InitField( &m_groupSmallContributions, "GroupSmallContributions", true, "Group Small Contributions" ); CAF_PDM_InitField( &m_smallContributionsThreshold, "SmallContributionsThreshold", 0.005, "Threshold" ); CAF_PDM_InitFieldNoDefault( &m_axisTitleFontSize, "AxisTitleFontSize", "Axis Title Font Size" ); CAF_PDM_InitFieldNoDefault( &m_axisValueFontSize, "AxisValueFontSize", "Axis Value Font Size" ); m_axisTitleFontSize = caf::FontTools::RelativeSize::Large; m_axisValueFontSize = caf::FontTools::RelativeSize::Medium; m_legendFontSize = caf::FontTools::RelativeSize::Medium; setAsPlotMdiWindow(); setShowWindow( false ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimWellAllocationOverTimePlot::~RimWellAllocationOverTimePlot() { removeMdiWindowFromMdiArea(); deleteViewWidget(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellAllocationOverTimePlot::setDescription( const QString& description ) { m_userName = description; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellAllocationOverTimePlot::setFromSimulationWell( RimSimWellInView* simWell ) { auto eclView = simWell->firstAncestorOrThisOfType(); auto eclCase = simWell->firstAncestorOrThisOfType(); m_case = eclCase; m_wellName = simWell->simWellData()->m_wellName; setValidTimeStepRangeForCase(); // Use the active flow diagnostics solutions, or the first one as default m_flowDiagSolution = eclView->cellResult()->flowDiagSolution(); if ( !m_flowDiagSolution ) { m_flowDiagSolution = m_case->defaultFlowDiagSolution(); } onLoadDataAndUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellAllocationOverTimePlot::setWellName( const QString& wellName ) { m_wellName = wellName; onLoadDataAndUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuPlotWidget* RimWellAllocationOverTimePlot::plotWidget() { return m_plotWidget; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimWellAllocationOverTimePlot::asciiDataForPlotExport() const { // Retrieve allocation over time data for wells RigWellAllocationOverTime allocationOverTime = createWellAllocationOverTime(); QString titleText = m_userName + "\n\n"; QString dataText = "Time Step\t"; for ( auto& [wellName, wellValues] : allocationOverTime.wellValuesMap() ) { dataText += wellName + "\t"; } dataText += "\n"; const QString dateFormatStr = dateFormatString(); for ( const auto& timeStep : allocationOverTime.timeStepDates() ) { dataText += timeStep.toString( dateFormatStr ) + "\t"; for ( auto& [wellName, wellValues] : allocationOverTime.wellValuesMap() ) { dataText += wellValues.count( timeStep ) == 0 ? QString::number( 0.0 ) : QString::number( wellValues.at( timeStep ) ); dataText += "\t"; } dataText += "\n"; } return titleText + dataText; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimWellAllocationOverTimePlot::description() const { return uiName(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QWidget* RimWellAllocationOverTimePlot::viewWidget() { return plotWidget(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QImage RimWellAllocationOverTimePlot::snapshotWindowContent() { QImage image; if ( m_plotWidget ) { QPixmap pix = m_plotWidget->grab(); image = pix.toImage(); } return image; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuPlotWidget* RimWellAllocationOverTimePlot::doCreatePlotViewWidget( QWidget* mainWindowParent ) { // If called multiple times? if ( m_plotWidget ) { return m_plotWidget; } auto* plotWidget = new RiuQwtPlotWidget( this, mainWindowParent ); new RiuQwtCurvePointTracker( plotWidget->qwtPlot(), true, nullptr ); // Remove event filter to disable unwanted highlighting on left click in plot. plotWidget->removeEventFilter(); caf::CmdFeatureMenuBuilder menuBuilder; menuBuilder << "RicShowPlotDataFeature"; new RiuContextMenuLauncher( plotWidget, menuBuilder ); m_plotWidget = plotWidget; RiuQwtPlotTools::enableDateBasedBottomXAxis( m_plotWidget->qwtPlot(), RiaPreferences::current()->dateFormat(), QString(), RiaDefines::DateFormatComponents::DATE_FORMAT_YEAR_MONTH_DAY, RiaDefines::TimeFormatComponents::TIME_FORMAT_NONE ); // Workaround: Enable axis title for bottom axis to activate correct font size for date axis m_plotWidget->setAxisTitleEnabled( RiuPlotAxis::defaultBottom(), true ); m_plotWidget->setAxisTitleEnabled( RiuPlotAxis::defaultLeft(), true ); if ( auto qwtPlot = m_plotWidget->qwtPlot() ) { auto scaleDraw = qwtPlot->axisScaleDraw( QwtAxis::XBottom ); if ( scaleDraw ) { scaleDraw->setLabelRotation( 30.0 ); } } m_plotWidget->insertLegend( RiuPlotWidget::Legend::RIGHT ); m_plotWidget->setAxisTitleText( RiuPlotAxis::defaultBottom(), "[Date]" ); updateLegend(); onLoadDataAndUpdate(); return m_plotWidget; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellAllocationOverTimePlot::deleteViewWidget() { if ( m_plotWidget != nullptr ) { m_plotWidget->hide(); m_plotWidget->setParent( nullptr ); delete m_plotWidget; m_plotWidget = nullptr; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellAllocationOverTimePlot::onLoadDataAndUpdate() { updateMdiWindowVisibility(); if ( m_plotWidget == nullptr || m_case == nullptr ) { return; } // If no 3D view is open, we have to make sure the case is opened if ( !m_case->ensureReservoirCaseIsOpen() ) { return; } updateFromWell(); // Must be called after curves are added to plot - to apply correct legend font size doUpdateLayout(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellAllocationOverTimePlot::doUpdateLayout() { if ( m_plotWidget == nullptr ) { return; } m_plotWidget->setPlotTitleFontSize( titleFontSize() ); m_plotWidget->setLegendFontSize( legendFontSize() ); m_plotWidget->setAxisFontsAndAlignment( RiuPlotAxis::defaultBottom(), axisTitleFontSize(), axisValueFontSize() ); m_plotWidget->setAxisFontsAndAlignment( RiuPlotAxis::defaultLeft(), axisTitleFontSize(), axisValueFontSize() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellAllocationOverTimePlot::updateFromWell() { if ( !m_plotWidget ) { return; } m_plotWidget->detachItems( RiuPlotWidget::PlotItemType::CURVE ); // Retrieve total fraction data for wells RigWellAllocationOverTime allocationOverTime = createWellAllocationOverTime(); std::vector allStackedValues( allocationOverTime.timeStepDates().size(), 0.0 ); // Negative z-position to show grid lines int zPos = -10000; for ( auto& [wellName, wellValues] : allocationOverTime.wellValuesMap() ) { cvf::Color3f color = m_flowDiagSolution ? m_flowDiagSolution->tracerColor( wellName ) : getTracerColor( wellName ); for ( size_t i = 0; i < allocationOverTime.timeStepDates().size(); ++i ) { const auto value = wellValues.at( allocationOverTime.timeStepDates()[i] ); allStackedValues[i] += value; } const auto qColor = QColor( color.rByte(), color.gByte(), color.bByte() ); const auto fillColor = RiaColorTools::blendQColors( qColor, QColor( Qt::white ), 3, 1 ); const QBrush fillBrush( fillColor, Qt::BrushStyle::SolidPattern ); auto interpolationType = m_flowValueType == FlowValueType::ACCUMULATED_FLOW_VOLUME || m_flowValueType == FlowValueType::ACCUMULATED_FLOW_VOLUME_PERCENTAGE ? RiuQwtPlotCurveDefines::CurveInterpolationEnum::INTERPOLATION_POINT_TO_POINT : RiuQwtPlotCurveDefines::CurveInterpolationEnum::INTERPOLATION_STEP_LEFT; RiuPlotCurve* curve = m_plotWidget->createPlotCurve( nullptr, wellName ); curve->setAppearance( RiuQwtPlotCurveDefines::LineStyleEnum::STYLE_SOLID, interpolationType, 2, qColor, fillBrush ); curve->setSamplesFromDatesAndYValues( allocationOverTime.timeStepDates(), allStackedValues, false ); curve->attachToPlot( m_plotWidget ); curve->showInPlot(); curve->setZ( zPos-- ); } QString descriptionText = QString( m_flowDiagSolution() ? "Well Allocation Over Time: " : "Well Flow Over Time: " ) + QString( "%1 (%2)" ).arg( m_wellName ).arg( m_case->caseUserDescription() ); QString valueTypeText = getValueTypeText(); QString newDescription = descriptionText + ", " + valueTypeText; setDescription( newDescription ); const auto windowTitle = RiaStdStringTools::removeHtmlTags( newDescription.toStdString() ); m_plotWidget->setWindowTitle( QString::fromStdString( windowTitle ) ); m_plotWidget->setPlotTitle( descriptionText + "
" + valueTypeText + "
" ); m_plotWidget->setAxisTitleText( RiuPlotAxis::defaultLeft(), valueTypeText ); if ( m_plotWidget->qwtPlot() ) { m_plotWidget->qwtPlot()->replot(); // Workaround: For some reason, the ordering of items is not updated correctly. Adjusting the z-value of curves // will trigger a replot of items in correct order. auto plotItemList = m_plotWidget->qwtPlot()->itemList(); for ( QwtPlotItem* plotItem : plotItemList ) { auto zValue = plotItem->z() + 1; plotItem->setZ( zValue ); } } } //-------------------------------------------------------------------------------------------------- /// Create well flow calculator per time step date, retrieve total tracer fractions and propagate /// well data for all time steps. If well does not exist for specific time step date - value is /// set to 0. //-------------------------------------------------------------------------------------------------- RigWellAllocationOverTime RimWellAllocationOverTimePlot::createWellAllocationOverTime() const { if ( !m_case ) { return RigWellAllocationOverTime( {}, {} ); } if ( m_selectedFromTimeStep() > m_selectedToTimeStep() ) { RiaLogging::error( QString( "Selected 'From Time Step' (%1) must be prior to selected 'To Time Step' (%2)" ) .arg( m_selectedFromTimeStep().toString( dateFormatString() ) ) .arg( m_selectedToTimeStep().toString( dateFormatString() ) ) ); return RigWellAllocationOverTime( {}, {} ); } const RigSimWellData* simWellData = m_case->eclipseCaseData()->findSimWellData( m_wellName ); if ( !simWellData ) { return RigWellAllocationOverTime( {}, {} ); } // Note: Threshold per calculator does not work for accumulated data - use no threshold for each calculator // and filter on threshold value after accumulating non-filtered values. const double smallContributionThreshold = m_groupSmallContributions() && m_flowValueType != FlowValueType::ACCUMULATED_FLOW_VOLUME && m_flowValueType != FlowValueType::ACCUMULATED_FLOW_VOLUME_PERCENTAGE ? m_smallContributionsThreshold : 0.0; const bool branchDetection = false; std::map timeStepAndCalculatorPairs; std::vector allTimeSteps = m_case->timeStepDates(); std::set selectedTimeSteps = getSelectedTimeSteps( allTimeSteps ); std::set excludedTimeSteps = std::set( m_excludeTimeSteps().begin(), m_excludeTimeSteps().end() ); // NOTE: Must iterate all time step dates for case to have correct time step index for simulation well data for ( size_t i = 0; i < allTimeSteps.size(); ++i ) { // Time step must be among selected time steps and not excluded if ( selectedTimeSteps.find( allTimeSteps[i] ) == selectedTimeSteps.end() || excludedTimeSteps.find( allTimeSteps[i] ) != excludedTimeSteps.end() ) { continue; } std::map*> tracerFractionCellValues = RimWellAllocationTools::findOrCreateRelevantTracerCellFractions( simWellData, m_flowDiagSolution, static_cast( i ) ); auto simWellBranches = RigSimulationWellCenterLineCalculator::calculateWellPipeCenterlineForTimeStep( m_case->eclipseCaseData(), simWellData, static_cast( i ), branchDetection, true ); const auto& [pipeBranchesCLCoords, pipeBranchesCellIds] = RigSimulationWellCenterLineCalculator::extractBranchData( simWellBranches ); if ( !tracerFractionCellValues.empty() && !pipeBranchesCLCoords.empty() ) { bool isProducer = ( simWellData->wellProductionType( i ) == RiaDefines::WellProductionType::PRODUCER || simWellData->wellProductionType( i ) == RiaDefines::WellProductionType::UNDEFINED_PRODUCTION_TYPE ); RigEclCellIndexCalculator cellIdxCalc( m_case->eclipseCaseData()->mainGrid(), m_case->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ), nullptr ); const auto calculator = RigAccWellFlowCalculator( pipeBranchesCLCoords, pipeBranchesCellIds, tracerFractionCellValues, cellIdxCalc, smallContributionThreshold, isProducer ); timeStepAndCalculatorPairs.emplace( allTimeSteps[i], calculator ); } else if ( !pipeBranchesCLCoords.empty() ) { const auto calculator = RigAccWellFlowCalculator( pipeBranchesCLCoords, pipeBranchesCellIds, smallContributionThreshold ); // NOTE: Would like to prevent this check. Is added due to calculator.tracerNames() gives // "oil", "water" and "gas" as return value when calculator.totalTracerFractions().size() = 0 if ( !calculator.totalTracerFractions().empty() ) { timeStepAndCalculatorPairs.emplace( allTimeSteps[i], calculator ); } } } // Create well allocation over time data const auto selectedTimeStepsVector = std::vector( selectedTimeSteps.begin(), selectedTimeSteps.end() ); RigWellAllocationOverTime wellAllocationOverTime( selectedTimeStepsVector, timeStepAndCalculatorPairs ); if ( m_flowValueType == FlowValueType::FLOW_RATE_PERCENTAGE ) { wellAllocationOverTime.fillWithFlowRatePercentageValues(); } else if ( m_flowValueType == FlowValueType::FLOW_RATE ) { wellAllocationOverTime.fillWithFlowRateValues(); } else if ( m_flowValueType == FlowValueType::FLOW_VOLUME ) { wellAllocationOverTime.fillWithFlowVolumeValues(); } else if ( m_flowValueType == FlowValueType::ACCUMULATED_FLOW_VOLUME ) { // Accumulated flow volume without threshold, and filter according to threshold after accumulating volumes const double actualSmallContributionThreshold = m_groupSmallContributions() ? m_smallContributionsThreshold : 0.0; wellAllocationOverTime.fillWithAccumulatedFlowVolumeValues( actualSmallContributionThreshold ); } else if ( m_flowValueType == FlowValueType::ACCUMULATED_FLOW_VOLUME_PERCENTAGE ) { // Accumulate flow volume percentages without threshold, and filter according to threshold after accumulating // values const double actualSmallContributionThreshold = m_groupSmallContributions() ? m_smallContributionsThreshold : 0.0; wellAllocationOverTime.fillWithAccumulatedFlowVolumePercentageValues( actualSmallContributionThreshold ); } else { CAF_ASSERT( "Not handled FlowValue type!" ); } return wellAllocationOverTime; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- caf::PdmFieldHandle* RimWellAllocationOverTimePlot::userDescriptionField() { return &m_userName; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellAllocationOverTimePlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { RimPlot::defineUiOrdering( uiConfigName, uiOrdering ); uiOrdering.add( &m_userName ); uiOrdering.add( &m_showPlotTitle ); caf::PdmUiGroup& dataGroup = *uiOrdering.addNewGroup( "Plot Data" ); dataGroup.add( &m_case ); dataGroup.add( &m_wellName ); caf::PdmUiGroup& timeStepGroup = *uiOrdering.addNewGroup( "Time Step" ); timeStepGroup.add( &m_selectedFromTimeStep ); timeStepGroup.add( &m_selectedToTimeStep ); timeStepGroup.add( &m_timeStepFilterMode ); if ( m_timeStepFilterMode == TimeStepFilterMode::TIME_STEP_COUNT ) { timeStepGroup.add( &m_timeStepCount ); } caf::PdmUiGroup& excludeTimeStepGroup = *timeStepGroup.addNewGroup( "Exclude Time Steps" ); excludeTimeStepGroup.add( &m_excludeTimeSteps ); excludeTimeStepGroup.setCollapsedByDefault(); timeStepGroup.add( &m_applyTimeStepSelections ); caf::PdmUiGroup& optionGroup = *uiOrdering.addNewGroup( "Options" ); optionGroup.add( &m_flowDiagSolution ); optionGroup.add( &m_flowValueType ); optionGroup.add( &m_groupSmallContributions ); optionGroup.add( &m_smallContributionsThreshold ); m_smallContributionsThreshold.uiCapability()->setUiReadOnly( !m_groupSmallContributions() ); caf::PdmUiGroup* fontGroup = uiOrdering.addNewGroup( "Fonts" ); fontGroup->setCollapsedByDefault(); RimPlotWindow::uiOrderingForFonts( uiConfigName, *fontGroup ); fontGroup->add( &m_axisTitleFontSize ); fontGroup->add( &m_axisValueFontSize ); uiOrdering.skipRemainingFields( true ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellAllocationOverTimePlot::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) { if ( field == &m_applyTimeStepSelections ) { caf::PdmUiPushButtonEditorAttribute* attrib = dynamic_cast( attribute ); if ( attrib ) { attrib->m_buttonText = "Apply"; } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellAllocationOverTimePlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) { RimPlot::fieldChangedByUi( changedField, oldValue, newValue ); if ( changedField == &m_case ) { if ( m_flowDiagSolution && m_case ) { m_flowDiagSolution = m_case->defaultFlowDiagSolution(); } else { m_flowDiagSolution = nullptr; } std::set sortedWellNames = findSortedWellNames(); if ( !sortedWellNames.size() ) m_wellName = ""; else if ( sortedWellNames.count( m_wellName() ) == 0 ) { m_wellName = *sortedWellNames.begin(); } setValidTimeStepRangeForCase(); onLoadDataAndUpdate(); } else if ( changedField == &m_wellName || changedField == &m_flowDiagSolution || changedField == &m_flowValueType || changedField == &m_groupSmallContributions || changedField == &m_smallContributionsThreshold ) { onLoadDataAndUpdate(); } else if ( changedField == &m_timeStepCount && m_timeStepFilterMode == TimeStepFilterMode::TIME_STEP_COUNT ) { m_excludeTimeSteps.setValue( {} ); } else if ( changedField == &m_applyTimeStepSelections ) { onLoadDataAndUpdate(); } else if ( changedField == &m_axisTitleFontSize || changedField == &m_axisValueFontSize ) { doUpdateLayout(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RimWellAllocationOverTimePlot::findSortedWellNames() { if ( m_case && m_case->eclipseCaseData() ) { return m_case->eclipseCaseData()->findSortedWellNames(); } return {}; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QList RimWellAllocationOverTimePlot::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) { QList options = RimPlot::calculateValueOptions( fieldNeedingOptions ); if ( !options.empty() ) { return options; } if ( fieldNeedingOptions == &m_case ) { auto resultCases = RimEclipseCaseTools::eclipseResultCases(); for ( RimEclipseResultCase* c : resultCases ) { options.push_back( caf::PdmOptionItemInfo( c->caseUserDescription(), c, false, c->uiIconProvider() ) ); } } else if ( fieldNeedingOptions == &m_wellName ) { const std::set sortedWellNames = findSortedWellNames(); caf::IconProvider simWellIcon( ":/Well.svg" ); for ( const auto& name : sortedWellNames ) { options.push_back( caf::PdmOptionItemInfo( name, name, false, simWellIcon ) ); } if ( options.size() == 0 ) { options.push_front( caf::PdmOptionItemInfo( "None", nullptr ) ); } } else if ( fieldNeedingOptions == &m_flowDiagSolution && m_case ) { RimFlowDiagSolution* defaultFlowSolution = m_case->defaultFlowDiagSolution(); options.push_back( caf::PdmOptionItemInfo( "Well Flow", nullptr ) ); if ( defaultFlowSolution ) { options.push_back( caf::PdmOptionItemInfo( "Allocation", defaultFlowSolution ) ); } } else if ( m_case && ( fieldNeedingOptions == &m_selectedFromTimeStep || fieldNeedingOptions == &m_selectedToTimeStep ) ) { const QString dateFormatStr = dateFormatString(); const auto timeSteps = m_case->timeStepDates(); for ( const auto& timeStep : timeSteps ) { options.push_back( caf::PdmOptionItemInfo( timeStep.toString( dateFormatStr ), timeStep ) ); } } else if ( m_case && ( fieldNeedingOptions == &m_excludeTimeSteps ) ) { const QString dateFormatStr = dateFormatString(); const auto selectedTimeSteps = getSelectedTimeSteps( m_case->timeStepDates() ); for ( const auto& timeStep : selectedTimeSteps ) { options.push_back( caf::PdmOptionItemInfo( timeStep.toString( dateFormatStr ), timeStep ) ); } } else if ( fieldNeedingOptions == &m_axisTitleFontSize || fieldNeedingOptions == &m_axisValueFontSize ) { options = caf::FontTools::relativeSizeValueOptions( RiaPreferences::current()->defaultPlotFontSize() ); } return options; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::Color3f RimWellAllocationOverTimePlot::getTracerColor( const QString& tracerName ) { if ( tracerName == RIG_FLOW_OIL_NAME ) return cvf::Color3f::DARK_GREEN; if ( tracerName == RIG_FLOW_GAS_NAME ) return cvf::Color3f::DARK_RED; if ( tracerName == RIG_FLOW_WATER_NAME ) return cvf::Color3f::BLUE; return cvf::Color3f::DARK_GRAY; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimWellAllocationOverTimePlot::getValueTypeText() const { RiaDefines::EclipseUnitSystem unitSet = m_case->eclipseCaseData()->unitsType(); RimWellLogFile::WellFlowCondition condition = m_flowDiagSolution ? RimWellLogFile::WELL_FLOW_COND_RESERVOIR : RimWellLogFile::WELL_FLOW_COND_STANDARD; if ( m_flowValueType == FlowValueType::FLOW_RATE_PERCENTAGE ) { QString conditionText = condition == RimWellLogFile::WELL_FLOW_COND_RESERVOIR ? "Reservoir" : "Surface"; return QString( "Percentage of %1 Flow Rate [%]" ).arg( conditionText ); } if ( m_flowValueType == FlowValueType::FLOW_RATE ) { return RimWellPlotTools::flowPlotAxisTitle( condition, unitSet ); } if ( m_flowValueType == FlowValueType::FLOW_VOLUME ) { return RimWellPlotTools::flowVolumePlotAxisTitle( condition, unitSet ); } if ( m_flowValueType == FlowValueType::ACCUMULATED_FLOW_VOLUME ) { return "Accumulated " + RimWellPlotTools::flowVolumePlotAxisTitle( condition, unitSet ); } if ( m_flowValueType == FlowValueType::ACCUMULATED_FLOW_VOLUME_PERCENTAGE ) { QString conditionText = condition == RimWellLogFile::WELL_FLOW_COND_RESERVOIR ? "Reservoir" : "Surface"; return QString( "Accumulated %1 Flow Volume Allocation [%]" ).arg( conditionText ); } return QString( "" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimWellAllocationOverTimePlot::dateFormatString() const { return RiaQDateTimeTools::dateFormatString( RiaPreferences::current()->dateFormat(), RiaDefines::DateFormatComponents::DATE_FORMAT_YEAR_MONTH_DAY ); } //-------------------------------------------------------------------------------------------------- /// Update selected "From Time Step" and "To Time Step" according to selected case. /// If both selected time steps exist for case, keep as is, otherwise select first and last time /// step in case. //-------------------------------------------------------------------------------------------------- void RimWellAllocationOverTimePlot::setValidTimeStepRangeForCase() { if ( m_case == nullptr || m_case->timeStepDates().size() == 0 ) { return; } auto isTimeStepInCase = [&]( const QDateTime timeStep ) -> bool { return std::find( m_case->timeStepDates().cbegin(), m_case->timeStepDates().cend(), timeStep ) != m_case->timeStepDates().cend(); }; if ( m_selectedFromTimeStep().isValid() && isTimeStepInCase( m_selectedFromTimeStep() ) && m_selectedToTimeStep().isValid() && isTimeStepInCase( m_selectedToTimeStep() ) ) { return; } m_selectedFromTimeStep = m_case->timeStepDates().front(); m_selectedToTimeStep = m_case->timeStepDates().back(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int RimWellAllocationOverTimePlot::axisTitleFontSize() const { return caf::FontTools::absolutePointSize( RiaPreferences::current()->defaultPlotFontSize(), m_axisTitleFontSize() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int RimWellAllocationOverTimePlot::axisValueFontSize() const { return caf::FontTools::absolutePointSize( RiaPreferences::current()->defaultPlotFontSize(), m_axisValueFontSize() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RimWellAllocationOverTimePlot::getSelectedTimeSteps( const std::vector& timeSteps ) const { const auto timeStepsInRange = RiaQDateTimeTools::getTimeStepsWithinSelectedRange( timeSteps, m_selectedFromTimeStep(), m_selectedToTimeStep() ); return m_timeStepFilterMode == TimeStepFilterMode::TIME_STEP_COUNT ? RiaQDateTimeTools::createEvenlyDistributedDates( timeStepsInRange, m_timeStepCount ) : std::set( timeStepsInRange.begin(), timeStepsInRange.end() ); }