Files
ResInsight/ApplicationLibCode/ProjectDataModel/Flow/RimWellConnectivityTable.cpp
T
Magne Sjaastad 546edadf35 Refactors font size handling using RimFontSizeField
Replaces direct PdmField usage for font sizes with the new
RimFontSizeField. This provides a consistent way to manage font
size options across different plot types.

The RimFontSizeField encapsulates the logic for generating font size
options based on the default plot font size from preferences. This
removes redundant code in various plot classes and ensures that font
size settings are synchronized with user preferences.
2025-08-25 12:43:36 +02:00

1505 lines
68 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// 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 <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimWellConnectivityTable.h"
#include "RiaPreferences.h"
#include "RiaQDateTimeTools.h"
#include "RiaStdStringTools.h"
#include "RiaWellFlowDefines.h"
#include "RigEclipseCaseData.h"
#include "Well/RigAccWellFlowCalculator.h"
#include "Well/RigSimWellData.h"
#include "Well/RigSimulationWellCenterLineCalculator.h"
#include "Well/RigWellAllocationOverTime.h"
#include "Well/RigWellResultFrame.h"
#include "RimEclipseCaseTools.h"
#include "RimEclipseCellColors.h"
#include "RimEclipseResultCase.h"
#include "RimEclipseView.h"
#include "RimFlowDiagSolution.h"
#include "RimFlowDiagnosticsTools.h"
#include "RimRegularLegendConfig.h"
#include "RimSimWellInView.h"
#include "RimTools.h"
#include "RimWellAllocationTools.h"
#include "RimWellLogLasFile.h"
#include "RimWellPlotTools.h"
#include "RiuMatrixPlotWidget.h"
#include "RiuTools.h"
#include "cafPdmUiComboBoxEditor.h"
#include "cafPdmUiPushButtonEditor.h"
#include "cafPdmUiToolButtonEditor.h"
#include "cafPdmUiTreeSelectionEditor.h"
#include "cvfScalarMapper.h"
#include <QPainter>
CAF_PDM_SOURCE_INIT( RimWellConnectivityTable, "RimWellConnectivityTable" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
namespace caf
{
template <>
void AppEnum<RimWellConnectivityTable::ViewFilterType>::setUp()
{
addItem( RimWellConnectivityTable::ViewFilterType::FILTER_BY_VISIBLE_PRODUCERS, "FILTER_BY_VISIBLE_PRODUCERS", "Filter Producers" );
addItem( RimWellConnectivityTable::ViewFilterType::FILTER_BY_VISIBLE_INJECTORS, "FILTER_BY_VISIBLE_INJECTORS", "Filter Injectors" );
addItem( RimWellConnectivityTable::ViewFilterType::CALCULATE_BY_VISIBLE_CELLS, "CALCULATE_BY_VISIBLE_CELLS", "Calculate By Visible Cells" );
setDefault( RimWellConnectivityTable::ViewFilterType::FILTER_BY_VISIBLE_PRODUCERS );
}
template <>
void AppEnum<RimWellConnectivityTable::TimeStepSelection>::setUp()
{
addItem( RimWellConnectivityTable::TimeStepSelection::SINGLE_TIME_STEP, "SINGLE_TIME_STEP", "Single Time Step" );
addItem( RimWellConnectivityTable::TimeStepSelection::TIME_STEP_RANGE, "TIME_STEP_RANGE", "Time Step Range" );
setDefault( RimWellConnectivityTable::TimeStepSelection::SINGLE_TIME_STEP );
}
template <>
void AppEnum<RimWellConnectivityTable::TimeSampleValueType>::setUp()
{
addItem( RimWellConnectivityTable::TimeSampleValueType::FLOW_RATE, "FLOW_RATE", "Flow Rate" );
addItem( RimWellConnectivityTable::TimeSampleValueType::FLOW_RATE_FRACTION, "FLOW_RATE_FRACTION", "Flow Rate Fraction" );
addItem( RimWellConnectivityTable::TimeSampleValueType::FLOW_RATE_PERCENTAGE, "FLOW_RATE_PERCENTAGE", "Flow Rate Percentage" );
setDefault( RimWellConnectivityTable::TimeSampleValueType::FLOW_RATE_FRACTION );
}
template <>
void AppEnum<RimWellConnectivityTable::TimeRangeValueType>::setUp()
{
addItem( RimWellConnectivityTable::TimeRangeValueType::ACCUMULATED_FLOW_VOLUME, "ACCUMULATED_FLOW_VOLUME", "Accumulated Flow Volume" );
addItem( RimWellConnectivityTable::TimeRangeValueType::ACCUMULATED_FLOW_VOLUME_FRACTION,
"ACCUMULATED_FLOW_VOLUME_FRACTION",
"Accumulated Flow Volume Fraction" );
addItem( RimWellConnectivityTable::TimeRangeValueType::ACCUMULATED_FLOW_VOLUME_PERCENTAGE,
"ACCUMULATED_FLOW_VOLUME_PERCENTAGE",
"Accumulated Flow Volume Percentage" );
setDefault( RimWellConnectivityTable::TimeRangeValueType::ACCUMULATED_FLOW_VOLUME_FRACTION );
}
template <>
void AppEnum<RimWellConnectivityTable::TimeStepRangeFilterMode>::setUp()
{
addItem( RimWellConnectivityTable::TimeStepRangeFilterMode::NONE, "NONE", "Show All Time Steps" );
addItem( RimWellConnectivityTable::TimeStepRangeFilterMode::TIME_STEP_COUNT, "TIME_STEP_COUNT", "Time Step Count" );
setDefault( RimWellConnectivityTable::TimeStepRangeFilterMode::TIME_STEP_COUNT );
}
template <>
void AppEnum<RimWellConnectivityTable::RangeType>::setUp()
{
addItem( RimWellConnectivityTable::RangeType::AUTOMATIC, "AUTOMATIC", "Min and Max in Table" );
addItem( RimWellConnectivityTable::RangeType::USER_DEFINED, "USER_DEFINED_MAX_MIN", "User Defined Range" );
setDefault( RimWellConnectivityTable::RangeType::AUTOMATIC );
}
} // namespace caf
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellConnectivityTable::RimWellConnectivityTable()
{
CAF_PDM_InitObject( "Producer/Injector Connectivity", ":/CorrelationMatrixPlot16x16.png" );
uiCapability()->setUiTreeChildrenHidden( true );
CAF_PDM_InitFieldNoDefault( &m_case, "CurveCase", "Case" );
m_case.uiCapability()->setUiTreeChildrenHidden( true );
CAF_PDM_InitFieldNoDefault( &m_cellFilterView, "VisibleCellView", "Filter by 3D View" );
CAF_PDM_InitFieldNoDefault( &m_viewFilterType, "ViewFilterType", " Filter type" );
CAF_PDM_InitFieldNoDefault( &m_flowDiagSolution, "FlowDiagSolution", "Flow Diag Solution" );
m_flowDiagSolution.uiCapability()->setUiHidden( true );
CAF_PDM_InitFieldNoDefault( &m_timeStepSelection, "TimeStepSelectopn", "Time Step Type" );
m_timeStepSelection.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() );
CAF_PDM_InitField( &m_selectProducersAndInjectorsForTimeSteps, "SelectProducersAndInjectorsForTimeSteps", true, "Select Wells For Time Step(s)" );
CAF_PDM_InitField( &m_thresholdValue, "ThresholdValue", 0.0, "Threshold" );
// Single time step configuration
CAF_PDM_InitFieldNoDefault( &m_timeSampleValueType, "TimeSampleValueType", "Value Type" );
m_timeSampleValueType.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() );
CAF_PDM_InitFieldNoDefault( &m_selectedTimeStep, "TimeStep", "Time Step" );
m_selectedTimeStep.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() );
// Time step range configuration
CAF_PDM_InitFieldNoDefault( &m_timeRangeValueType, "TimeRangeValueType", "Value Type" );
m_timeRangeValueType.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() );
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::configureEditorLabelLeft( &m_applyTimeStepSelections );
// Producer/Injector tracer configuration
CAF_PDM_InitFieldNoDefault( &m_selectedProducerTracersUiField, "SelectedProducerTracers", "Producer Tracers" );
m_selectedProducerTracersUiField.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
CAF_PDM_InitFieldNoDefault( &m_selectedInjectorTracersUiField, "SelectedInjectorTracers", "Injector Tracers" );
m_selectedInjectorTracersUiField.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
CAF_PDM_InitField( &m_syncSelectedInjectorsFromProducerSelection, "SyncSelectedProdInj", false, "Synch Communicators ->" );
m_syncSelectedInjectorsFromProducerSelection.uiCapability()->setUiEditorTypeName( caf::PdmUiToolButtonEditor::uiEditorTypeName() );
CAF_PDM_InitField( &m_syncSelectedProducersFromInjectorSelection, "SyncSelectedInjProd", false, "<- Synch Communicators" );
m_syncSelectedProducersFromInjectorSelection.uiCapability()->setUiEditorTypeName( caf::PdmUiToolButtonEditor::uiEditorTypeName() );
CAF_PDM_InitFieldNoDefault( &m_applySelectedInectorProducerTracers, "ApplySelectedInectorProducerTracers", "" );
caf::PdmUiPushButtonEditor::configureEditorLabelLeft( &m_applySelectedInectorProducerTracers );
// Table settings
CAF_PDM_InitField( &m_showValueLabels, "ShowValueLabels", false, "Show Value Labels" );
// Font control
CAF_PDM_InitFieldNoDefault( &m_axisTitleFontSize, "AxisTitleFontSize", "Axis Title Font Size" );
CAF_PDM_InitFieldNoDefault( &m_axisLabelFontSize, "AxisLabelFontSize", "Axis Label Font Size" );
CAF_PDM_InitFieldNoDefault( &m_valueLabelFontSize, "ValueLabelFontSize", "Value Label Font Size" );
m_axisTitleFontSize = caf::FontTools::RelativeSize::Large;
m_axisLabelFontSize = caf::FontTools::RelativeSize::Medium;
CAF_PDM_InitFieldNoDefault( &m_legendConfig, "LegendConfig", "" );
m_legendConfig = new RimRegularLegendConfig();
m_legendConfig->setShowLegend( true );
m_legendConfig->setAutomaticRanges( 0.0, 100.0, 0.0, 100.0 );
m_legendConfig->setColorLegend( RimRegularLegendConfig::mapToColorLegend( RimRegularLegendConfig::ColorRangesType::HEAT_MAP ) );
CAF_PDM_InitField( &m_mappingType, "MappingType", RimRegularLegendConfig::MappingType::LINEAR_CONTINUOUS, "Mapping Type" );
caf::AppEnum<RimRegularLegendConfig::MappingType>::setEnumSubset( &m_mappingType,
{ RimRegularLegendConfig::MappingType::LINEAR_DISCRETE,
RimRegularLegendConfig::MappingType::LINEAR_CONTINUOUS,
RimRegularLegendConfig::MappingType::LOG10_CONTINUOUS,
RimRegularLegendConfig::MappingType::LOG10_DISCRETE } );
CAF_PDM_InitFieldNoDefault( &m_rangeType, "RangeType", "Range Type" );
setLegendsVisible( true );
setAsPlotMdiWindow();
setShowWindow( false );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellConnectivityTable::~RimWellConnectivityTable()
{
if ( isMdiWindow() ) removeMdiWindowFromMdiArea();
cleanupBeforeClose();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::setFromSimulationWell( RimSimWellInView* simWell )
{
if ( !simWell ) return;
auto eclView = simWell->firstAncestorOrThisOfType<RimEclipseView>();
auto eclCase = simWell->firstAncestorOrThisOfType<RimEclipseResultCase>();
m_case = eclCase;
// Set valid single time step and time step range selections based on case
setValidTimeStepSelectionsForCase();
if ( eclCase )
{
m_selectedTimeStep = eclCase->timeStepDates().at( eclView->currentTimeStep() );
}
// Use the active flow diagnostics solutions, or the first one as default
if ( eclView )
{
m_flowDiagSolution = eclView->cellResult()->flowDiagSolution();
if ( ( m_flowDiagSolution == nullptr ) && ( m_case != nullptr ) )
{
m_flowDiagSolution = m_case->defaultFlowDiagSolution();
}
}
connectViewCellVisibilityChangedToSlot( m_cellFilterView );
// Set single time step and current selected time step from active view
m_timeStepSelection = TimeStepSelection::SINGLE_TIME_STEP;
if ( m_cellFilterView )
{
setWellSelectionFromViewFilter();
}
else
{
setSelectedProducersAndInjectorsForSingleTimeStep();
}
onLoadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::cleanupBeforeClose()
{
if ( m_matrixPlotWidget )
{
m_matrixPlotWidget->qwtPlot()->detachItems();
m_matrixPlotWidget->setParent( nullptr );
delete m_matrixPlotWidget;
m_matrixPlotWidget = nullptr;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
RimPlotWindow::fieldChangedByUi( changedField, oldValue, newValue );
if ( changedField == &m_case )
{
if ( m_case )
{
m_flowDiagSolution = m_case->defaultFlowDiagSolution();
m_cellFilterView = !m_case->views().empty() ? dynamic_cast<RimEclipseView*>( m_case->views().front() ) : nullptr;
}
else
{
m_flowDiagSolution = nullptr;
m_cellFilterView = nullptr;
}
setWellSelectionFromViewFilter();
setValidTimeStepSelectionsForCase();
onLoadDataAndUpdate();
}
else if ( changedField == &m_flowDiagSolution || changedField == &m_thresholdValue )
{
if ( m_thresholdValue() < 0.0 )
{
m_matrixPlotWidget->setInvalidValueRange( m_thresholdValue(), 0.0 );
}
else
{
m_matrixPlotWidget->setInvalidValueRange( 0.0, m_thresholdValue() );
}
onLoadDataAndUpdate();
}
else if ( changedField == &m_cellFilterView )
{
// Disconnect signal/slots for previous cellFilterView
PdmObjectHandle* prevValue = oldValue.value<caf::PdmPointer<PdmObjectHandle>>().rawPtr();
auto* prevCellFilterView = dynamic_cast<RimEclipseView*>( prevValue );
disconnectViewCellVisibilityChangedFromSlots( prevCellFilterView );
if ( m_cellFilterView )
{
// Connect signal/slots for current cellFilterView
connectViewCellVisibilityChangedToSlot( m_cellFilterView );
// Update well selections using current view filter type for the active cell filter view
setWellSelectionFromViewFilter();
}
if ( !m_cellFilterView && m_selectProducersAndInjectorsForTimeSteps )
{
if ( m_timeStepSelection() == TimeStepSelection::SINGLE_TIME_STEP )
{
setSelectedProducersAndInjectorsForSingleTimeStep();
}
if ( m_timeStepSelection() == TimeStepSelection::TIME_STEP_RANGE )
{
setSelectedProducersAndInjectorsForTimeStepRange();
}
}
else
{
m_selectedProducerTracersUiField.setValueWithFieldChanged( {} );
m_selectedInjectorTracersUiField.setValueWithFieldChanged( {} );
}
onLoadDataAndUpdate();
}
else if ( changedField == &m_viewFilterType )
{
setWellSelectionFromViewFilter();
onLoadDataAndUpdate();
}
else if ( changedField == &m_syncSelectedInjectorsFromProducerSelection )
{
syncSelectedInjectorsFromProducerSelection();
}
else if ( changedField == &m_syncSelectedProducersFromInjectorSelection )
{
syncSelectedProducersFromInjectorSelection();
}
else if ( m_matrixPlotWidget && changedField == &m_showValueLabels )
{
m_matrixPlotWidget->setShowValueLabel( m_showValueLabels );
}
else if ( changedField == &m_selectProducersAndInjectorsForTimeSteps )
{
if ( m_selectProducersAndInjectorsForTimeSteps && m_timeStepSelection == TimeStepSelection::SINGLE_TIME_STEP )
{
setSelectedProducersAndInjectorsForSingleTimeStep();
onLoadDataAndUpdate();
}
else if ( m_selectProducersAndInjectorsForTimeSteps && m_timeStepSelection == TimeStepSelection::TIME_STEP_RANGE )
{
setSelectedProducersAndInjectorsForTimeStepRange();
onLoadDataAndUpdate();
}
}
else if ( changedField == &m_selectedTimeStep || changedField == &m_timeSampleValueType ||
( changedField == &m_timeStepSelection && m_timeStepSelection() == TimeStepSelection::SINGLE_TIME_STEP ) )
{
// Update selected prod/injectors based on selected time step if no view filter is active
if ( !m_cellFilterView && m_selectProducersAndInjectorsForTimeSteps )
{
setSelectedProducersAndInjectorsForSingleTimeStep();
}
if ( m_cellFilterView )
{
setWellSelectionFromViewFilter();
}
// For singe time step - no apply buttons due to low computation time
onLoadDataAndUpdate();
}
else if ( changedField == &m_selectedFromTimeStep || changedField == &m_selectedToTimeStep ||
( changedField == &m_timeStepSelection && m_timeStepSelection() == TimeStepSelection::TIME_STEP_RANGE ) )
{
// Update selected prod/injectors based on time step range if no view filter is active
if ( !m_cellFilterView && m_selectProducersAndInjectorsForTimeSteps )
{
setSelectedProducersAndInjectorsForTimeStepRange();
}
if ( m_cellFilterView )
{
setWellSelectionFromViewFilter();
}
}
else if ( changedField == &m_applyTimeStepSelections || changedField == &m_applySelectedInectorProducerTracers )
{
// For time step range - depends on apply buttons to prevent unwanted loading of large amount of data
onLoadDataAndUpdate();
}
else if ( changedField == &m_timeStepCount && m_timeStepFilterMode == TimeStepRangeFilterMode::TIME_STEP_COUNT )
{
m_excludeTimeSteps.setValue( {} );
}
else if ( m_matrixPlotWidget && ( changedField == &m_titleFontSize || changedField == &m_legendFontSize ||
changedField == &m_axisTitleFontSize || changedField == &m_axisLabelFontSize ) )
{
m_matrixPlotWidget->setPlotTitleFontSize( titleFontSize() );
m_matrixPlotWidget->setLegendFontSize( legendFontSize() );
m_matrixPlotWidget->setAxisTitleFontSize( axisTitleFontSize() );
m_matrixPlotWidget->setAxisLabelFontSize( axisLabelFontSize() );
}
else if ( changedField == &m_valueLabelFontSize && m_matrixPlotWidget )
{
m_matrixPlotWidget->setValueFontSize( valueLabelFontSize() );
}
else if ( changedField == &m_rangeType && m_legendConfig )
{
auto rangeMode = m_rangeType == RangeType::AUTOMATIC ? RimLegendConfig::RangeModeType::AUTOMATIC_ALLTIMESTEPS
: RimLegendConfig::RangeModeType::USER_DEFINED;
m_legendConfig->setRangeMode( rangeMode );
m_legendConfig->updateTickCountAndUserDefinedRange();
onLoadDataAndUpdate();
}
else if ( changedField == &m_mappingType && m_legendConfig )
{
m_legendConfig->setMappingMode( m_mappingType() );
if ( m_rangeType == RangeType::AUTOMATIC )
{
m_legendConfig->updateTickCountAndUserDefinedRange();
}
onLoadDataAndUpdate();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::childFieldChangedByUi( const caf::PdmFieldHandle* changedChildField )
{
onLoadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
RimViewWindow::defineUiOrdering( uiConfigName, uiOrdering );
caf::PdmUiGroup& dataGroup = *uiOrdering.addNewGroup( "Plot Data" );
dataGroup.add( &m_case );
dataGroup.add( &m_cellFilterView );
if ( m_cellFilterView )
{
dataGroup.add( &m_viewFilterType );
}
dataGroup.add( &m_flowDiagSolution );
dataGroup.add( &m_timeStepSelection );
if ( !m_cellFilterView )
{
dataGroup.add( &m_selectProducersAndInjectorsForTimeSteps );
}
dataGroup.add( &m_thresholdValue );
caf::PdmUiGroup& flowDiagConfigGroup = *uiOrdering.addNewGroup( "Flow Diagnostics Configuration" );
if ( m_timeStepSelection() == TimeStepSelection::SINGLE_TIME_STEP )
{
flowDiagConfigGroup.add( &m_timeSampleValueType );
flowDiagConfigGroup.add( &m_selectedTimeStep );
}
else if ( m_timeStepSelection() == TimeStepSelection::TIME_STEP_RANGE )
{
flowDiagConfigGroup.add( &m_timeRangeValueType );
flowDiagConfigGroup.add( &m_selectedFromTimeStep );
flowDiagConfigGroup.add( &m_selectedToTimeStep );
flowDiagConfigGroup.add( &m_timeStepFilterMode );
if ( m_timeStepFilterMode == TimeStepRangeFilterMode::TIME_STEP_COUNT )
{
flowDiagConfigGroup.add( &m_timeStepCount );
}
caf::PdmUiGroup& excludeTimeStepGroup = *flowDiagConfigGroup.addNewGroup( "Exclude Time Steps" );
excludeTimeStepGroup.add( &m_excludeTimeSteps );
excludeTimeStepGroup.setCollapsedByDefault();
flowDiagConfigGroup.add( &m_applyTimeStepSelections );
}
caf::PdmUiGroup* selectionGroup = uiOrdering.addNewGroup( "Tracer Selection" );
caf::PdmUiGroup* producerGroup = selectionGroup->addNewGroup( "Producers" );
producerGroup->add( &m_selectedProducerTracersUiField );
producerGroup->add( &m_syncSelectedInjectorsFromProducerSelection );
caf::PdmUiGroup* injectorGroup = selectionGroup->addNewGroup( "Injectors", { .newRow = false } );
injectorGroup->add( &m_selectedInjectorTracersUiField );
injectorGroup->add( &m_syncSelectedProducersFromInjectorSelection );
selectionGroup->add( &m_applySelectedInectorProducerTracers );
caf::PdmUiGroup* tableSettingsGroup = uiOrdering.addNewGroup( "Table Settings" );
tableSettingsGroup->add( &m_showValueLabels );
m_legendConfig->uiOrdering( "FlagAndColorsOnly", *tableSettingsGroup );
tableSettingsGroup->add( &m_mappingType );
tableSettingsGroup->add( &m_rangeType );
m_legendConfig->uiOrdering( "UserDefinedMinMaxOnly", *tableSettingsGroup );
caf::PdmUiGroup* fontGroup = uiOrdering.addNewGroup( "Fonts" );
fontGroup->setCollapsedByDefault();
RimPlotWindow::uiOrderingForFonts( uiConfigName, *fontGroup );
fontGroup->add( &m_axisTitleFontSize );
fontGroup->add( &m_axisLabelFontSize );
fontGroup->add( &m_valueLabelFontSize );
uiOrdering.skipRemainingFields( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
{
if ( field == &m_applyTimeStepSelections || field == &m_applySelectedInectorProducerTracers )
{
caf::PdmUiPushButtonEditorAttribute* attrib = dynamic_cast<caf::PdmUiPushButtonEditorAttribute*>( attribute );
if ( attrib )
{
attrib->m_buttonText = "Apply";
}
}
if ( field == &m_selectedTimeStep || field == &m_selectedFromTimeStep || field == &m_selectedToTimeStep )
{
RiuTools::enableUpDownArrowsForComboBox( attribute );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::onLoadDataAndUpdate()
{
updateMdiWindowVisibility();
if ( m_matrixPlotWidget == 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;
}
// Clear matrix plot
m_matrixPlotWidget->clearPlotData();
// Create well flow diagnostics data - with selected production wells (no selection -> no filtering)
std::set<QString> selectedProductionWells =
std::set<QString>( m_selectedProducerTracersUiField().begin(), m_selectedProducerTracersUiField().end() );
const std::map<QString, RigWellAllocationOverTime> productionWellsAllocationOverTime =
createProductionWellsAllocationOverTimeMap( selectedProductionWells );
// Retrieve union of injection wells across selected producers
std::set<QString> injectionWellsUnion;
for ( const auto& [prodWellName, prodWellData] : productionWellsAllocationOverTime )
{
for ( const auto& [injectionWell, values] : prodWellData.wellValuesMap() )
{
injectionWellsUnion.insert( injectionWell );
}
}
// Detect matrix column headers
std::set<QString> selectedInjectors =
std::set<QString>( m_selectedInjectorTracersUiField().begin(), m_selectedInjectorTracersUiField().end() );
const std::set<QString> injectionWells = selectedInjectors.empty() ? injectionWellsUnion : selectedInjectors;
const std::vector<QString> columnHeaders( injectionWells.begin(), injectionWells.end() );
// Store rows where minimum one injector well value is above threshold
std::map<QString, std::vector<double>> filteredRows;
for ( const auto& [prodWellName, prodWellData] : productionWellsAllocationOverTime )
{
bool hasValueAboveThreshold = false;
std::vector<double> rowValues = std::vector<double>( columnHeaders.size(), 0.0 );
for ( const auto& [injectionWell, values] : prodWellData.wellValuesMap() )
{
auto itr = std::find( columnHeaders.begin(), columnHeaders.end(), injectionWell );
if ( itr == columnHeaders.end() ) continue;
auto index = static_cast<size_t>( std::distance( columnHeaders.begin(), itr ) );
if ( index > rowValues.size() - 1 ) continue;
// Get value at last time step
const auto value = values.rbegin()->second;
rowValues[index] = value;
if ( value > m_thresholdValue() )
{
hasValueAboveThreshold = true;
}
}
if ( rowValues.empty() || !hasValueAboveThreshold ) continue;
filteredRows.emplace( prodWellName, rowValues );
}
// Detect columns with one or more value above threshold
std::set<size_t> columnIndicesToInclude;
for ( const auto& [rowName, rowValues] : filteredRows )
{
for ( size_t i = 0; i < rowValues.size(); i++ )
{
if ( rowValues[i] > m_thresholdValue() )
{
columnIndicesToInclude.insert( i );
}
}
}
// Filter column headers based on column indexes to include
std::vector<QString> filteredColumnHeaders;
for ( size_t i = 0; i < columnHeaders.size(); ++i )
{
if ( columnIndicesToInclude.contains( i ) )
{
filteredColumnHeaders.push_back( columnHeaders[i] );
}
}
// Fill matrix plot widget with filtered rows/columns
double maxValue = 0.0;
m_matrixPlotWidget->setColumnHeaders( filteredColumnHeaders );
double posClosestToZeroValue = std::numeric_limits<double>::max();
double negClosestToZeroValue = std::numeric_limits<double>::lowest();
for ( const auto& [rowName, rowValues] : filteredRows )
{
// Add columns with values above threshold
std::vector<double> columns;
for ( size_t i = 0; i < rowValues.size(); ++i )
{
if ( !columnIndicesToInclude.contains( i ) ) continue;
maxValue = maxValue < rowValues[i] ? rowValues[i] : maxValue;
columns.push_back( rowValues[i] );
// Find positive and negative value closest to zero
if ( rowValues[i] > 0.0 && rowValues[i] < posClosestToZeroValue ) posClosestToZeroValue = rowValues[i];
if ( rowValues[i] < 0.0 && rowValues[i] > negClosestToZeroValue ) negClosestToZeroValue = rowValues[i];
}
m_matrixPlotWidget->setRowValues( rowName, columns );
}
if ( negClosestToZeroValue == std::numeric_limits<double>::lowest() ) negClosestToZeroValue = -0.1;
if ( posClosestToZeroValue == std::numeric_limits<double>::max() ) posClosestToZeroValue = 0.1;
// Set ranges using max value
if ( m_legendConfig )
{
const auto [min, max] = createLegendMinMaxValues( maxValue );
m_legendConfig->setAutomaticRanges( min, max, 0.0, 0.0 );
m_legendConfig->setClosestToZeroValues( posClosestToZeroValue, negClosestToZeroValue, posClosestToZeroValue, negClosestToZeroValue );
}
// Set titles and font sizes
m_matrixPlotWidget->setPlotTitle( createTableTitle() );
m_matrixPlotWidget->setRowTitle( "Producer wells" );
m_matrixPlotWidget->setColumnTitle( "Injection wells" );
m_matrixPlotWidget->setPlotTitleFontSize( titleFontSize() );
m_matrixPlotWidget->setLegendFontSize( legendFontSize() );
m_matrixPlotWidget->setAxisTitleFontSize( axisTitleFontSize() );
m_matrixPlotWidget->setAxisLabelFontSize( axisLabelFontSize() );
const auto windowTitle = RiaStdStringTools::removeHtmlTags( createTableTitle().toStdString() );
m_matrixPlotWidget->setWindowTitle( QString::fromStdString( windowTitle ) );
m_matrixPlotWidget->createPlot();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<caf::PdmOptionItemInfo> RimWellConnectivityTable::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
{
QList<caf::PdmOptionItemInfo> options = RimPlotWindow::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_cellFilterView && m_case() )
{
options.push_back( caf::PdmOptionItemInfo( "Disabled", nullptr ) );
for ( RimEclipseView* view : m_case()->reservoirViews() )
{
CVF_ASSERT( view && "Really always should have a valid view pointer in ReservoirViews" );
options.push_back( caf::PdmOptionItemInfo( view->name(), view, false, view->uiIconProvider() ) );
}
}
else if ( m_case && fieldNeedingOptions == &m_flowDiagSolution )
{
RimFlowDiagSolution* defaultFlowSolution = m_case->defaultFlowDiagSolution();
if ( defaultFlowSolution )
{
options.push_back( caf::PdmOptionItemInfo( "Default Flow Diag Solution", defaultFlowSolution ) );
}
}
else if ( m_case && ( fieldNeedingOptions == &m_selectedFromTimeStep || fieldNeedingOptions == &m_selectedToTimeStep ||
fieldNeedingOptions == &m_selectedTimeStep ) )
{
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_selectedInjectorTracersUiField && m_flowDiagSolution() )
{
const bool isInjector = true;
options = RimFlowDiagnosticsTools::calcOptionsForSelectedTracerField( m_flowDiagSolution(), isInjector );
if ( !options.empty() )
{
options.push_back( caf::PdmOptionItemInfo( RiaDefines::reservoirTracerName(), RiaDefines::reservoirTracerName() ) );
}
}
else if ( fieldNeedingOptions == &m_selectedProducerTracersUiField && m_flowDiagSolution() )
{
const bool isInjector = false;
options = RimFlowDiagnosticsTools::calcOptionsForSelectedTracerField( m_flowDiagSolution(), isInjector );
}
return options;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QWidget* RimWellConnectivityTable::viewWidget()
{
return m_matrixPlotWidget;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QImage RimWellConnectivityTable::snapshotWindowContent()
{
QImage image;
if ( m_matrixPlotWidget )
{
QPixmap pix = m_matrixPlotWidget->grab();
image = pix.toImage();
}
return image;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::zoomAll()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QWidget* RimWellConnectivityTable::createViewWidget( QWidget* mainWindowParent )
{
m_matrixPlotWidget = new RiuMatrixPlotWidget( this, m_legendConfig, mainWindowParent );
m_matrixPlotWidget->setShowValueLabel( m_showValueLabels );
m_matrixPlotWidget->setUseInvalidValueColor( true );
return m_matrixPlotWidget;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::deleteViewWidget()
{
cleanupBeforeClose();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimWellConnectivityTable::description() const
{
return QString();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::doRenderWindowContent( QPaintDevice* paintDevice )
{
// Note: Not tested yet!
if ( m_matrixPlotWidget )
{
QPainter painter( paintDevice );
m_matrixPlotWidget->render( &painter );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RimWellConnectivityTable::axisTitleFontSize() const
{
return caf::FontTools::absolutePointSize( RiaPreferences::current()->defaultPlotFontSize(), m_axisTitleFontSize() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RimWellConnectivityTable::axisLabelFontSize() const
{
return caf::FontTools::absolutePointSize( RiaPreferences::current()->defaultPlotFontSize(), m_axisLabelFontSize() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RimWellConnectivityTable::valueLabelFontSize() const
{
return caf::FontTools::absolutePointSize( RiaPreferences::current()->defaultPlotFontSize(), m_valueLabelFontSize() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QString> RimWellConnectivityTable::getProductionWellNames() const
{
if ( !m_flowDiagSolution ) return {};
std::vector<QString> productionWellNames;
if ( m_timeStepSelection == TimeStepSelection::SINGLE_TIME_STEP )
{
const auto timeStepIndex = getTimeStepIndex( m_selectedTimeStep, m_case->timeStepDates() );
productionWellNames = timeStepIndex < 0 ? std::vector<QString>()
: RimFlowDiagnosticsTools::producerTracersInTimeStep( m_flowDiagSolution(), timeStepIndex );
}
else if ( m_timeStepSelection == TimeStepSelection::TIME_STEP_RANGE )
{
const auto selectedTimeSteps = getSelectedTimeSteps( m_case->timeStepDates() );
productionWellNames = getProductionWellNamesAtTimeSteps( selectedTimeSteps );
}
return productionWellNames;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QString> RimWellConnectivityTable::getProductionWellNamesAtTimeSteps( const std::set<QDateTime>& timeSteps ) const
{
if ( !m_flowDiagSolution ) return {};
std::set<QString> productionWellNames;
const auto allTimeSteps = m_case->timeStepDates();
for ( const auto& timeStep : timeSteps )
{
const auto timeStepIndex = getTimeStepIndex( timeStep, allTimeSteps );
if ( timeStepIndex < 0 ) continue;
const auto producers = RimFlowDiagnosticsTools::producerTracersInTimeStep( m_flowDiagSolution(), timeStepIndex );
for ( const auto& producer : producers )
{
productionWellNames.insert( producer );
}
}
return std::vector<QString>( productionWellNames.begin(), productionWellNames.end() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimWellConnectivityTable::createTableTitle() const
{
RiaDefines::EclipseUnitSystem unitSet = m_case->eclipseCaseData()->unitsType();
RimWellLogLasFile::WellFlowCondition condition = RimWellLogLasFile::WELL_FLOW_COND_RESERVOIR;
auto timeSampleValueTypeText = [&]() -> QString
{
if ( m_timeSampleValueType == TimeSampleValueType::FLOW_RATE_PERCENTAGE )
{
return QString( "Percentage of Total Reservoir Flow Rate [%]" );
}
if ( m_timeSampleValueType == TimeSampleValueType::FLOW_RATE_FRACTION )
{
return QString( "Fraction of Total Reservoir Flow Rate" );
}
if ( m_timeSampleValueType == TimeSampleValueType::FLOW_RATE )
{
return "Total " + RimWellPlotTools::flowPlotAxisTitle( condition, unitSet );
}
return QString();
};
auto timeRangeValueTypeText = [&]() -> QString
{
if ( m_timeRangeValueType() == TimeRangeValueType::ACCUMULATED_FLOW_VOLUME )
{
return "Accumulated Total " + RimWellPlotTools::flowVolumePlotAxisTitle( condition, unitSet );
}
if ( m_timeRangeValueType() == TimeRangeValueType::ACCUMULATED_FLOW_VOLUME_PERCENTAGE )
{
return QString( "Accumulated Total Reservoir Flow Volume Allocation [%]" );
}
if ( m_timeRangeValueType() == TimeRangeValueType::ACCUMULATED_FLOW_VOLUME_FRACTION )
{
return QString( "Accumulated Total Reservoir Flow Volume Allocation Fraction" );
}
return QString();
};
if ( m_timeStepSelection() == TimeStepSelection::SINGLE_TIME_STEP )
{
return QString( "%1 (%2)<br>Date: %3</br>" )
.arg( timeSampleValueTypeText() )
.arg( m_case->caseUserDescription() )
.arg( m_selectedTimeStep().toString( dateFormatString() ) );
}
if ( m_timeStepSelection() == TimeStepSelection::TIME_STEP_RANGE )
{
return QString( "%1 (%2)<br>Date range: [%3, %4], Number of time steps: %5</br>" )
.arg( timeRangeValueTypeText() )
.arg( m_case->caseUserDescription() )
.arg( m_selectedFromTimeStep().toString( dateFormatString() ) )
.arg( m_selectedToTimeStep().toString( dateFormatString() ) )
.arg( m_timeStepCount() );
}
return "";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<double, double> RimWellConnectivityTable::createLegendMinMaxValues( const double maxTableValue ) const
{
const bool isSingleTimeStep = m_timeStepSelection == TimeStepSelection::SINGLE_TIME_STEP;
const bool isTimeStepRange = m_timeStepSelection == TimeStepSelection::TIME_STEP_RANGE;
if ( ( isSingleTimeStep && m_timeSampleValueType == TimeSampleValueType::FLOW_RATE_PERCENTAGE ) ||
( isTimeStepRange && m_timeRangeValueType == TimeRangeValueType::ACCUMULATED_FLOW_VOLUME_PERCENTAGE ) )
{
return std::make_pair( 0.0, 100.0 );
}
if ( ( isSingleTimeStep && m_timeSampleValueType == TimeSampleValueType::FLOW_RATE_FRACTION ) ||
( isTimeStepRange && m_timeRangeValueType == TimeRangeValueType::ACCUMULATED_FLOW_VOLUME_FRACTION ) )
{
return std::make_pair( 0.0, 1.0 );
}
return std::make_pair( 0.0, maxTableValue );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::setValidTimeStepSelectionsForCase()
{
setValidSingleTimeStepForCase();
setValidTimeStepRangeForCase();
}
//--------------------------------------------------------------------------------------------------
/// Update selected "Time Step" according to selected case.
/// If selected time steps exist for case, keep as is, otherwise select first time step in case.
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::setValidSingleTimeStepForCase()
{
if ( !m_case || m_case->timeStepDates().empty() )
{
return;
}
if ( m_selectedTimeStep().isValid() && isTimeStepInCase( m_selectedTimeStep() ) )
{
return;
}
m_selectedTimeStep = m_case->timeStepDates().front();
}
//--------------------------------------------------------------------------------------------------
/// 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 RimWellConnectivityTable::setValidTimeStepRangeForCase()
{
if ( !m_case || m_case->timeStepDates().empty() )
{
return;
}
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();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimWellConnectivityTable::isTimeStepInCase( const QDateTime& timeStep ) const
{
if ( !m_case ) return false;
return std::find( m_case->timeStepDates().cbegin(), m_case->timeStepDates().cend(), timeStep ) != m_case->timeStepDates().cend();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RimWellConnectivityTable::getTimeStepIndex( const QDateTime timeStep, const std::vector<QDateTime> timeSteps ) const
{
auto itr = std::find( timeSteps.begin(), timeSteps.end(), timeStep );
if ( itr == timeSteps.end() ) return -1;
return static_cast<int>( std::distance( timeSteps.begin(), itr ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::setSelectedProducersAndInjectorsForSingleTimeStep()
{
if ( !m_case || !m_flowDiagSolution || !m_selectedTimeStep().isValid() ) return;
const int timeStepIndex = getTimeStepIndex( m_selectedTimeStep, m_case->timeStepDates() );
if ( timeStepIndex < 0 )
{
m_selectedProducerTracersUiField.setValueWithFieldChanged( {} );
m_selectedInjectorTracersUiField.setValueWithFieldChanged( {} );
return;
}
const std::vector<QString> producerVec = RimFlowDiagnosticsTools::producerTracersInTimeStep( m_flowDiagSolution, timeStepIndex );
std::vector<QString> injectorVec = RimFlowDiagnosticsTools::injectorTracersInTimeStep( m_flowDiagSolution, timeStepIndex );
injectorVec.push_back( RiaDefines::reservoirTracerName() );
// No filtering if all producers/injectors are selected and assign to UI-elements
m_selectedProducerTracersUiField.setValueWithFieldChanged( producerVec );
m_selectedInjectorTracersUiField.setValueWithFieldChanged( injectorVec );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::setSelectedProducersAndInjectorsForTimeStepRange()
{
if ( !m_case || !m_flowDiagSolution || !m_selectedTimeStep().isValid() ) return;
const auto allTimeSteps = m_case->timeStepDates();
const std::set<QDateTime> selectedTimeSteps = getSelectedTimeSteps( allTimeSteps );
const std::set<QDateTime> excludedTimeSteps = std::set( m_excludeTimeSteps().begin(), m_excludeTimeSteps().end() );
std::set<QString> producerSet;
std::set<QString> injectorSet;
for ( const auto& timeStep : selectedTimeSteps )
{
if ( excludedTimeSteps.contains( timeStep ) ) continue;
const auto timeStepIndex = getTimeStepIndex( timeStep, allTimeSteps );
if ( timeStepIndex < 0 ) continue;
const auto timeStepProducers = RimFlowDiagnosticsTools::producerTracersInTimeStep( m_flowDiagSolution, timeStepIndex );
const auto timeStepInjectors = RimFlowDiagnosticsTools::injectorTracersInTimeStep( m_flowDiagSolution, timeStepIndex );
// Insert vectors into set
std::copy( timeStepProducers.begin(), timeStepProducers.end(), std::inserter( producerSet, producerSet.end() ) );
std::copy( timeStepInjectors.begin(), timeStepInjectors.end(), std::inserter( injectorSet, injectorSet.end() ) );
}
injectorSet.insert( RiaDefines::reservoirTracerName() );
// Assign to UI-elements
m_selectedProducerTracersUiField.setValueWithFieldChanged( std::vector<QString>( producerSet.begin(), producerSet.end() ) );
m_selectedInjectorTracersUiField.setValueWithFieldChanged( std::vector<QString>( injectorSet.begin(), injectorSet.end() ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::syncSelectedInjectorsFromProducerSelection()
{
std::vector<QString> injectors;
if ( m_timeStepSelection == TimeStepSelection::SINGLE_TIME_STEP )
{
const auto timeStepIndex = getTimeStepIndex( m_selectedTimeStep, m_case->timeStepDates() );
const auto newInjectors = timeStepIndex < 0
? std::set<QString, RimFlowDiagnosticsTools::TracerComp>()
: RimFlowDiagnosticsTools::setOfInjectorTracersFromProducers( m_flowDiagSolution(),
m_selectedProducerTracersUiField(),
timeStepIndex );
injectors = std::vector<QString>( newInjectors.begin(), newInjectors.end() );
injectors.push_back( RiaDefines::reservoirTracerName() );
}
if ( m_timeStepSelection == TimeStepSelection::TIME_STEP_RANGE )
{
const auto allTimeSteps = m_case->timeStepDates();
const std::set<QDateTime> selectedTimeSteps = getSelectedTimeSteps( allTimeSteps );
std::vector<int> timeStepIndices;
for ( const auto& timeStep : selectedTimeSteps )
{
const auto timeStepIndex = getTimeStepIndex( timeStep, allTimeSteps );
if ( timeStepIndex < 0 ) continue;
timeStepIndices.push_back( timeStepIndex );
}
const auto newInjectors = RimFlowDiagnosticsTools::setOfInjectorTracersFromProducers( m_flowDiagSolution(),
m_selectedProducerTracersUiField(),
timeStepIndices );
injectors = std::vector<QString>( newInjectors.begin(), newInjectors.end() );
injectors.push_back( RiaDefines::reservoirTracerName() );
}
m_selectedInjectorTracersUiField.setValueWithFieldChanged( injectors );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::syncSelectedProducersFromInjectorSelection()
{
std::vector<QString> producers;
if ( m_timeStepSelection == TimeStepSelection::SINGLE_TIME_STEP )
{
const auto timeStepIndex = getTimeStepIndex( m_selectedTimeStep, m_case->timeStepDates() );
const auto newProducers = timeStepIndex < 0
? std::set<QString, RimFlowDiagnosticsTools::TracerComp>()
: RimFlowDiagnosticsTools::setOfProducerTracersFromInjectors( m_flowDiagSolution(),
m_selectedInjectorTracersUiField(),
timeStepIndex );
producers = std::vector<QString>( newProducers.begin(), newProducers.end() );
}
if ( m_timeStepSelection == TimeStepSelection::TIME_STEP_RANGE )
{
const auto allTimeSteps = m_case->timeStepDates();
std::set<QDateTime> selectedTimeSteps = getSelectedTimeSteps( allTimeSteps );
std::vector<int> timeStepIndices;
for ( const auto& timeStep : selectedTimeSteps )
{
const auto timeStepIndex = getTimeStepIndex( timeStep, allTimeSteps );
if ( timeStepIndex < 0 ) continue;
timeStepIndices.push_back( timeStepIndex );
}
const auto newProducers = RimFlowDiagnosticsTools::setOfProducerTracersFromInjectors( m_flowDiagSolution(),
m_selectedInjectorTracersUiField(),
timeStepIndices );
producers = std::vector<QString>( newProducers.begin(), newProducers.end() );
producers.push_back( RiaDefines::reservoirTracerName() );
}
m_selectedProducerTracersUiField.setValueWithFieldChanged( producers );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::setProducerSelectionFromViewFilterAndSynchInjectors()
{
if ( !m_cellFilterView || !m_case || !m_case->eclipseCaseData() ) return;
m_selectedProducerTracersUiField.setValueWithFieldChanged(
getViewFilteredWellNamesFromFilterType( ViewFilterType::FILTER_BY_VISIBLE_PRODUCERS ) );
syncSelectedInjectorsFromProducerSelection();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::setInjectorSelectionFromViewFilterAndSynchProducers()
{
if ( !m_cellFilterView || !m_case || !m_case->eclipseCaseData() ) return;
m_selectedInjectorTracersUiField.setValueWithFieldChanged(
getViewFilteredWellNamesFromFilterType( ViewFilterType::FILTER_BY_VISIBLE_INJECTORS ) );
syncSelectedProducersFromInjectorSelection();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::setWellSelectionFromViewFilter()
{
if ( !m_cellFilterView || m_viewFilterType == ViewFilterType::CALCULATE_BY_VISIBLE_CELLS )
{
m_selectedProducerTracersUiField.setValueWithFieldChanged( {} );
m_selectedInjectorTracersUiField.setValueWithFieldChanged( {} );
return;
}
if ( m_viewFilterType == ViewFilterType::FILTER_BY_VISIBLE_PRODUCERS )
{
setProducerSelectionFromViewFilterAndSynchInjectors();
}
else if ( m_viewFilterType == ViewFilterType::FILTER_BY_VISIBLE_INJECTORS )
{
setInjectorSelectionFromViewFilterAndSynchProducers();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QString> RimWellConnectivityTable::getViewFilteredWellNamesFromFilterType( ViewFilterType filterType ) const
{
if ( !m_cellFilterView || !m_case || !m_case->eclipseCaseData() ) return {};
auto isProductionTypeOfFilterType = [&]( RiaDefines::WellProductionType productionType ) -> bool
{
if ( filterType == ViewFilterType::FILTER_BY_VISIBLE_PRODUCERS )
{
return productionType == RiaDefines::WellProductionType::PRODUCER;
}
if ( filterType == ViewFilterType::FILTER_BY_VISIBLE_INJECTORS )
{
return RiaDefines::isInjector( productionType );
}
return false;
};
// Retrieve cell visibilities and time step for cell filter view
const auto timeStepIndex = m_cellFilterView->currentTimeStep();
const auto* cellVisibilities = m_cellFilterView->currentTotalCellVisibility().p();
RigEclCellIndexCalculator cellIdxCalc( m_case->eclipseCaseData()->mainGrid(),
m_case->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ),
cellVisibilities );
std::vector<QString> productionWells;
const auto& wellResults = m_case->eclipseCaseData()->wellResults();
for ( const auto& wellResult : wellResults )
{
const auto productionType = wellResult->wellProductionType( timeStepIndex );
if ( !isProductionTypeOfFilterType( productionType ) )
{
continue;
}
const auto productionWellResultFrame = wellResult->staticWellResultFrame();
for ( const auto& resultPoint : productionWellResultFrame->allResultPoints() )
{
if ( !resultPoint.isCell() ) continue;
if ( cellIdxCalc.isCellVisible( resultPoint.gridIndex(), resultPoint.cellIndex() ) )
{
productionWells.push_back( wellResult->m_wellName );
break;
}
}
}
return productionWells;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::onCellVisibilityChanged( const SignalEmitter* emitter )
{
if ( m_cellFilterView && m_viewFilterType == ViewFilterType::FILTER_BY_VISIBLE_PRODUCERS )
{
setProducerSelectionFromViewFilterAndSynchInjectors();
}
else if ( m_cellFilterView && m_viewFilterType == ViewFilterType::FILTER_BY_VISIBLE_INJECTORS )
{
setInjectorSelectionFromViewFilterAndSynchProducers();
}
onLoadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::connectViewCellVisibilityChangedToSlot( RimEclipseView* view )
{
if ( !view ) return;
view->cellVisibilityChanged.connect( this, &RimWellConnectivityTable::onCellVisibilityChanged );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::disconnectViewCellVisibilityChangedFromSlots( RimEclipseView* view )
{
if ( !view ) return;
view->cellVisibilityChanged.disconnect( this );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::map<QString, RigWellAllocationOverTime>
RimWellConnectivityTable::createProductionWellsAllocationOverTimeMap( const std::set<QString>& selectedProductionWells ) const
{
if ( !m_case || !m_flowDiagSolution )
{
return std::map<QString, RigWellAllocationOverTime>();
}
std::map<QString, RigWellAllocationOverTime> wellAllocationOverTimeMap;
// Create well allocation over time for each production well
std::vector<QString> prodWellNames = getProductionWellNames();
for ( const auto& wellName : prodWellNames )
{
if ( !selectedProductionWells.empty() && !selectedProductionWells.contains( wellName ) ) continue;
const RigSimWellData* simWellData = m_case->eclipseCaseData()->findSimWellData( wellName );
if ( !simWellData ) continue;
wellAllocationOverTimeMap.emplace( wellName, createWellAllocationOverTime( simWellData ) );
}
return wellAllocationOverTimeMap;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigWellAllocationOverTime RimWellConnectivityTable::createWellAllocationOverTime( const RigSimWellData* simWellData ) const
{
if ( !m_case || !simWellData || !m_flowDiagSolution )
{
return RigWellAllocationOverTime( {}, {} );
}
const std::vector<QDateTime> allTimeSteps = m_case->timeStepDates();
std::map<QDateTime, RigAccWellFlowCalculator> timeStepAndCalculatorPairs;
if ( m_timeStepSelection() == TimeStepSelection::TIME_STEP_RANGE )
{
std::set<QDateTime> selectedTimeSteps = getSelectedTimeSteps( allTimeSteps );
std::set<QDateTime> excludedTimeSteps = std::set( m_excludeTimeSteps().begin(), m_excludeTimeSteps().end() );
for ( const auto& timeStep : allTimeSteps )
{
if ( !selectedTimeSteps.contains( timeStep ) || excludedTimeSteps.contains( timeStep ) )
{
continue;
}
const auto timeStepIndex = getTimeStepIndex( timeStep, allTimeSteps );
if ( timeStepIndex < 0 ) continue;
createAndEmplaceTimeStepAndCalculatorPairInMap( timeStepAndCalculatorPairs, timeStep, timeStepIndex, simWellData );
}
// Create well allocation over time data
const auto selectedTimeStepsVector = std::vector( selectedTimeSteps.begin(), selectedTimeSteps.end() );
RigWellAllocationOverTime wellAllocationOverTime( selectedTimeStepsVector, timeStepAndCalculatorPairs );
// NB: No threshold when generating calculators - filtering must be done after collecting data
// for each producer!
const double smallContributionThreshold = 0.0;
if ( m_timeRangeValueType == TimeRangeValueType::ACCUMULATED_FLOW_VOLUME )
{
wellAllocationOverTime.fillWithAccumulatedFlowVolumeValues( smallContributionThreshold );
}
else if ( m_timeRangeValueType == TimeRangeValueType::ACCUMULATED_FLOW_VOLUME_FRACTION )
{
wellAllocationOverTime.fillWithAccumulatedFlowVolumeFractionValues( smallContributionThreshold );
}
else if ( m_timeRangeValueType == TimeRangeValueType::ACCUMULATED_FLOW_VOLUME_PERCENTAGE )
{
wellAllocationOverTime.fillWithAccumulatedFlowVolumePercentageValues( smallContributionThreshold );
}
return wellAllocationOverTime;
}
if ( m_timeStepSelection() == TimeStepSelection::SINGLE_TIME_STEP )
{
const auto timeStepIndex = getTimeStepIndex( m_selectedTimeStep(), allTimeSteps );
if ( timeStepIndex < 0 ) return RigWellAllocationOverTime( {}, {} );
createAndEmplaceTimeStepAndCalculatorPairInMap( timeStepAndCalculatorPairs, m_selectedTimeStep(), timeStepIndex, simWellData );
// Create well allocation over time data
RigWellAllocationOverTime wellAllocationOverTime( { m_selectedTimeStep() }, timeStepAndCalculatorPairs );
if ( m_timeSampleValueType == TimeSampleValueType::FLOW_RATE )
{
wellAllocationOverTime.fillWithFlowRateValues();
}
else if ( m_timeSampleValueType == TimeSampleValueType::FLOW_RATE_FRACTION )
{
wellAllocationOverTime.fillWithFlowRateFractionValues();
}
else if ( m_timeSampleValueType == TimeSampleValueType::FLOW_RATE_PERCENTAGE )
{
wellAllocationOverTime.fillWithFlowRatePercentageValues();
}
return wellAllocationOverTime;
}
return RigWellAllocationOverTime( {}, {} );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellConnectivityTable::createAndEmplaceTimeStepAndCalculatorPairInMap( std::map<QDateTime, RigAccWellFlowCalculator>& rTimeStepAndCalculatorPairs,
const QDateTime timeStep,
int timeStepIndex,
const RigSimWellData* simWellData ) const
{
if ( timeStepIndex < 0 ) return;
// NB: No threshold when generating calculators!
const double smallContributionThreshold = 0.0;
const bool branchDetection = false;
auto simWellBranches = RigSimulationWellCenterLineCalculator::calculateWellPipeCenterlineForTimeStep( m_case->eclipseCaseData(),
simWellData,
timeStepIndex,
branchDetection,
true );
const auto& [pipeBranchesCLCoords, pipeBranchesCellIds] = RigSimulationWellCenterLineCalculator::extractBranchData( simWellBranches );
std::map<QString, const std::vector<double>*> tracerFractionCellValues =
RimWellAllocationTools::findOrCreateRelevantTracerCellFractions( simWellData, m_flowDiagSolution, timeStepIndex );
if ( !tracerFractionCellValues.empty() && !pipeBranchesCLCoords.empty() )
{
bool isProducer = ( simWellData->wellProductionType( timeStepIndex ) == RiaDefines::WellProductionType::PRODUCER ||
simWellData->wellProductionType( timeStepIndex ) == RiaDefines::WellProductionType::UNDEFINED_PRODUCTION_TYPE );
// Retrieve cell visibilities for valid cell filter view
const auto* cellVisibilities = m_cellFilterView && m_viewFilterType == ViewFilterType::CALCULATE_BY_VISIBLE_CELLS
? m_cellFilterView->currentTotalCellVisibility().p()
: nullptr;
RigEclCellIndexCalculator cellIdxCalc( m_case->eclipseCaseData()->mainGrid(),
m_case->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ),
cellVisibilities );
const auto calculator = RigAccWellFlowCalculator( pipeBranchesCLCoords,
pipeBranchesCellIds,
tracerFractionCellValues,
cellIdxCalc,
smallContributionThreshold,
isProducer );
rTimeStepAndCalculatorPairs.emplace( timeStep, calculator );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<QDateTime> RimWellConnectivityTable::getSelectedTimeSteps( const std::vector<QDateTime>& timeSteps ) const
{
const auto timeStepsInRange =
RiaQDateTimeTools::getTimeStepsWithinSelectedRange( timeSteps, m_selectedFromTimeStep(), m_selectedToTimeStep() );
return m_timeStepFilterMode == TimeStepRangeFilterMode::TIME_STEP_COUNT
? RiaQDateTimeTools::createEvenlyDistributedDates( timeStepsInRange, m_timeStepCount )
: std::set( timeStepsInRange.begin(), timeStepsInRange.end() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimWellConnectivityTable::dateFormatString() const
{
return RiaQDateTimeTools::dateFormatString( RiaPreferences::current()->dateFormat(),
RiaDefines::DateFormatComponents::DATE_FORMAT_YEAR_MONTH_DAY );
}