Producer/Injector Connectivity Table (#9928)

- Crated new RimMatrixPlotWidget to show table data
- Create RimWellConnectivityTable for showing Producer/Injector connectivity table data
- Rename RimWellAllocationOverTimeCollection to RigWellAllocationOverTime for well allocation over time data storage
- Created heatmap color palette
- Move utils from RimWellAllocationOverTimePlot to RiaQDateTimeTools
- Create RimFlowDiagnosticsTools for producer/injector well utility functions


---------

Co-authored-by: jorgenherje <jorgenherje@users.noreply.github.com>
This commit is contained in:
Jørgen Herje
2023-03-21 08:32:38 +01:00
committed by GitHub
parent 37abe17582
commit 535811cc4f
36 changed files with 2692 additions and 294 deletions

View File

@@ -38,6 +38,7 @@ CAF_PDM_SOURCE_INIT( RimCellFilterCollection, "CellFilterCollection", "RimCellFi
///
//--------------------------------------------------------------------------------------------------
RimCellFilterCollection::RimCellFilterCollection()
: filtersChanged( this )
{
CAF_PDM_InitScriptableObject( "Cell Filters", ":/CellFilter.png" );
@@ -357,6 +358,8 @@ void RimCellFilterCollection::onFilterUpdated( const SignalEmitter* emitter )
view->scheduleGeometryRegen( RANGE_FILTERED_INACTIVE );
view->scheduleCreateDisplayModelAndRedraw();
filtersChanged.send();
}
//--------------------------------------------------------------------------------------------------

View File

@@ -21,6 +21,7 @@
#include "cafPdmChildArrayField.h"
#include "cafPdmField.h"
#include "cafPdmObject.h"
#include "cafSignal.h"
class RimCellFilter;
class RimCellRangeFilter;
@@ -45,6 +46,8 @@ public:
RimCellFilterCollection();
~RimCellFilterCollection() override;
caf::Signal<> filtersChanged;
RimPolygonFilter* addNewPolygonFilter( RimCase* srcCase );
RimUserDefinedFilter* addNewUserDefinedFilter( RimCase* srcCase );
RimCellRangeFilter* addNewCellRangeFilter( RimCase* srcCase, int gridIndex, int sliceDirection = -1, int defaultSlice = -1 );

View File

@@ -16,7 +16,8 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimWellDistributionPlotCollection.h
${CMAKE_CURRENT_LIST_DIR}/RimWellAllocationOverTimePlot.h
${CMAKE_CURRENT_LIST_DIR}/RimWellAllocationTools.h
${CMAKE_CURRENT_LIST_DIR}/RimWellAllocationOverTimeCollection.h
${CMAKE_CURRENT_LIST_DIR}/RimWellConnectivityTable.h
${CMAKE_CURRENT_LIST_DIR}/RimFlowDiagnosticsTools.h
)
set(SOURCE_GROUP_SOURCE_FILES
@@ -37,7 +38,8 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimWellDistributionPlotCollection.cpp
${CMAKE_CURRENT_LIST_DIR}/RimWellAllocationOverTimePlot.cpp
${CMAKE_CURRENT_LIST_DIR}/RimWellAllocationTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RimWellAllocationOverTimeCollection.cpp
${CMAKE_CURRENT_LIST_DIR}/RimWellConnectivityTable.cpp
${CMAKE_CURRENT_LIST_DIR}/RimFlowDiagnosticsTools.cpp
)
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})

View File

@@ -0,0 +1,216 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RimFlowDiagnosticsTools.h"
#include "RigFlowDiagResults.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimFlowDiagnosticsTools::TracerComp::operator()( const QString& lhs, const QString& rhs ) const
{
if ( !lhs.endsWith( "-XF" ) && rhs.endsWith( "-XF" ) )
{
return true;
}
else if ( lhs.endsWith( "-XF" ) && !rhs.endsWith( "-XF" ) )
{
return false;
}
else
{
return lhs < rhs;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<caf::PdmOptionItemInfo> RimFlowDiagnosticsTools::calcOptionsForSelectedTracerField( RimFlowDiagSolution* flowSol, bool isInjector )
{
if ( !flowSol ) return {};
QList<caf::PdmOptionItemInfo> options;
std::set<QString, TracerComp> sortedTracers = setOfTracersOfType( flowSol, isInjector );
for ( const QString& tracerName : sortedTracers )
{
QString postfix;
RimFlowDiagSolution::TracerStatusType status = flowSol->tracerStatusOverall( tracerName );
if ( status == RimFlowDiagSolution::TracerStatusType::VARYING )
{
postfix = " [I/P]";
}
else if ( status == RimFlowDiagSolution::TracerStatusType::UNDEFINED )
{
postfix = " [U]";
}
options.push_back( caf::PdmOptionItemInfo( tracerName + postfix, tracerName ) );
}
return options;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<QString, RimFlowDiagnosticsTools::TracerComp> RimFlowDiagnosticsTools::setOfTracersOfType( RimFlowDiagSolution* flowSol,
bool isInjector )
{
if ( !flowSol ) return {};
std::set<QString, TracerComp> sortedTracers;
std::vector<QString> tracerNames = flowSol->tracerNames();
for ( const QString& tracerName : tracerNames )
{
RimFlowDiagSolution::TracerStatusType status = flowSol->tracerStatusOverall( tracerName );
bool includeTracer = status == RimFlowDiagSolution::TracerStatusType::VARYING ||
status == RimFlowDiagSolution::TracerStatusType::UNDEFINED;
includeTracer |= isInjector && status == RimFlowDiagSolution::TracerStatusType::INJECTOR;
includeTracer |= !isInjector && status == RimFlowDiagSolution::TracerStatusType::PRODUCER;
if ( includeTracer )
{
sortedTracers.insert( tracerName );
}
}
return sortedTracers;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QString> RimFlowDiagnosticsTools::producerTracersInTimeStep( RimFlowDiagSolution* flowSol, int timeStepIndex )
{
return tracersOfStatusInTimeStep( flowSol, RimFlowDiagSolution::TracerStatusType::PRODUCER, timeStepIndex );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QString> RimFlowDiagnosticsTools::injectorTracersInTimeStep( RimFlowDiagSolution* flowSol, int timeStepIndex )
{
return tracersOfStatusInTimeStep( flowSol, RimFlowDiagSolution::TracerStatusType::INJECTOR, timeStepIndex );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QString> RimFlowDiagnosticsTools::tracersOfStatusInTimeStep( RimFlowDiagSolution* flowSol,
RimFlowDiagSolution::TracerStatusType status,
int timeStepIndex )
{
if ( !flowSol || timeStepIndex < 0 ) return {};
std::vector<QString> tracers;
for ( const auto& tracer : flowSol->tracerNames() )
{
if ( status == flowSol->tracerStatusInTimeStep( tracer, timeStepIndex ) )
{
tracers.push_back( tracer );
}
}
return tracers;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<QString, RimFlowDiagnosticsTools::TracerComp>
RimFlowDiagnosticsTools::setOfInjectorTracersFromProducers( RimFlowDiagSolution* flowSol,
const std::vector<QString>& producerTracers,
std::vector<int> timeStepIndices )
{
if ( !flowSol ) return {};
const double epsilon = 1.0e-8;
const bool isInjector = true;
std::set<QString, TracerComp> communicatingInjectors;
std::set<QString, TracerComp> injectors = RimFlowDiagnosticsTools::setOfTracersOfType( flowSol, isInjector );
for ( const QString& producer : producerTracers )
{
for ( const QString& injector : injectors )
{
for ( const auto& timeStepIndex : timeStepIndices )
{
std::pair<double, double> commFluxes =
flowSol->flowDiagResults()->injectorProducerPairFluxes( injector.toStdString(), producer.toStdString(), timeStepIndex );
if ( std::abs( commFluxes.first ) > epsilon || std::abs( commFluxes.second ) > epsilon )
{
communicatingInjectors.insert( injector );
}
}
}
}
return communicatingInjectors;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<QString, RimFlowDiagnosticsTools::TracerComp>
RimFlowDiagnosticsTools::setOfInjectorTracersFromProducers( RimFlowDiagSolution* flowSol,
const std::vector<QString>& producerTracers,
int timeStepIndex )
{
const auto timeStepIndices = std::vector<int>( { timeStepIndex } );
return setOfInjectorTracersFromProducers( flowSol, producerTracers, timeStepIndices );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<QString, RimFlowDiagnosticsTools::TracerComp>
RimFlowDiagnosticsTools::setOfProducerTracersFromInjectors( RimFlowDiagSolution* flowSol,
const std::vector<QString>& injectorTracers,
std::vector<int> timeStepIndices )
{
if ( !flowSol ) return {};
const double epsilon = 1.0e-8;
const bool isInjector = false;
std::set<QString, TracerComp> communicatingProducers;
std::set<QString, TracerComp> producers = RimFlowDiagnosticsTools::setOfTracersOfType( flowSol, isInjector );
for ( const QString& injector : injectorTracers )
{
for ( const QString& producer : producers )
{
for ( const auto& timeStepIndex : timeStepIndices )
{
std::pair<double, double> commFluxes =
flowSol->flowDiagResults()->injectorProducerPairFluxes( injector.toStdString(), producer.toStdString(), timeStepIndex );
if ( std::abs( commFluxes.first ) > epsilon || std::abs( commFluxes.second ) > epsilon )
{
communicatingProducers.insert( producer );
}
}
}
}
return communicatingProducers;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<QString, RimFlowDiagnosticsTools::TracerComp>
RimFlowDiagnosticsTools::setOfProducerTracersFromInjectors( RimFlowDiagSolution* flowSol,
const std::vector<QString>& injectorTracers,
int timeStepIndex )
{
const auto timeStepIndices = std::vector<int>( { timeStepIndex } );
return setOfProducerTracersFromInjectors( flowSol, injectorTracers, timeStepIndices );
}

View File

@@ -0,0 +1,61 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "RimFlowDiagSolution.h"
#include "cafPdmUiItem.h"
#include <QList>
#include <QString>
//==================================================================================================
///
///
//==================================================================================================
namespace RimFlowDiagnosticsTools
{
// --- Structures ---
struct TracerComp
{
bool operator()( const QString& lhs, const QString& rhs ) const;
};
// --- Methods ---
QList<caf::PdmOptionItemInfo> calcOptionsForSelectedTracerField( RimFlowDiagSolution* flowSol, bool isInjector );
std::set<QString, TracerComp> setOfTracersOfType( RimFlowDiagSolution* flowSol, bool isInjector );
std::vector<QString> producerTracersInTimeStep( RimFlowDiagSolution* flowSol, int timeStepIndex );
std::vector<QString> injectorTracersInTimeStep( RimFlowDiagSolution* flowSol, int timeStepIndex );
std::vector<QString> tracersOfStatusInTimeStep( RimFlowDiagSolution* flowSol, RimFlowDiagSolution::TracerStatusType status, int timeStepIndex );
std::set<QString, RimFlowDiagnosticsTools::TracerComp> setOfInjectorTracersFromProducers( RimFlowDiagSolution* flowSol,
const std::vector<QString>& producerTracers,
std::vector<int> timeStepIndices );
std::set<QString, RimFlowDiagnosticsTools::TracerComp>
setOfInjectorTracersFromProducers( RimFlowDiagSolution* flowSol, const std::vector<QString>& producerTracers, int timeStepIndex );
std::set<QString, RimFlowDiagnosticsTools::TracerComp> setOfProducerTracersFromInjectors( RimFlowDiagSolution* flowSol,
const std::vector<QString>& injectorTracers,
std::vector<int> timeStepIndices );
std::set<QString, RimFlowDiagnosticsTools::TracerComp>
setOfProducerTracersFromInjectors( RimFlowDiagSolution* flowSol, const std::vector<QString>& injectorTracers, int timeStepIndex );
}; // namespace RimFlowDiagnosticsTools

View File

@@ -22,6 +22,7 @@
#include "RimProject.h"
#include "RimWellAllocationOverTimePlot.h"
#include "RimWellAllocationPlot.h"
#include "RimWellConnectivityTable.h"
#include "RimWellDistributionPlotCollection.h"
#include "cafProgressInfo.h"
@@ -39,6 +40,9 @@ RimFlowPlotCollection::RimFlowPlotCollection()
CAF_PDM_InitFieldNoDefault( &m_flowCharacteristicsPlot, "FlowCharacteristicsPlot", "" );
m_flowCharacteristicsPlot.uiCapability()->setUiTreeHidden( true );
CAF_PDM_InitFieldNoDefault( &m_defaultWellConnectivityTable, "DefaultWellConnectivityTable", "" );
m_defaultWellConnectivityTable.uiCapability()->setUiTreeHidden( true );
CAF_PDM_InitFieldNoDefault( &m_defaultWellAllocOverTimePlot, "DefaultWellAllocationOverTimePlot", "" );
m_defaultWellAllocOverTimePlot.uiCapability()->setUiTreeHidden( true );
@@ -72,6 +76,7 @@ void RimFlowPlotCollection::deleteAllPlots()
m_defaultWellAllocPlot->removeFromMdiAreaAndDeleteViewWidget();
delete m_defaultWellAllocPlot();
}
delete m_defaultWellConnectivityTable;
delete m_defaultWellAllocOverTimePlot;
delete m_flowCharacteristicsPlot;
delete m_wellDistributionPlotCollection;
@@ -90,6 +95,9 @@ void RimFlowPlotCollection::loadDataAndUpdateAllPlots()
if ( m_defaultWellAllocPlot ) m_defaultWellAllocPlot->loadDataAndUpdate();
plotProgress.incrementProgress();
if ( m_defaultWellConnectivityTable ) m_defaultWellConnectivityTable->loadDataAndUpdate();
plotProgress.incrementProgress();
if ( m_defaultWellAllocOverTimePlot ) m_defaultWellAllocOverTimePlot->loadDataAndUpdate();
plotProgress.incrementProgress();
@@ -123,6 +131,7 @@ size_t RimFlowPlotCollection::plotCount() const
{
size_t plotCount = 0;
plotCount += m_defaultWellAllocPlot ? 1 : 0;
plotCount += m_defaultWellConnectivityTable ? 1 : 0;
plotCount += m_defaultWellAllocOverTimePlot ? 1 : 0;
plotCount += m_storedWellAllocPlots.size();
plotCount += m_storedFlowCharacteristicsPlots.size();
@@ -145,6 +154,21 @@ void RimFlowPlotCollection::addFlowCharacteristicsPlotToStoredPlots( RimFlowChar
m_storedFlowCharacteristicsPlots.push_back( plot );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellConnectivityTable* RimFlowPlotCollection::defaultWellConnectivityTable()
{
if ( !m_defaultWellConnectivityTable )
{
m_defaultWellConnectivityTable = new RimWellConnectivityTable;
}
this->updateConnectedEditors();
return m_defaultWellConnectivityTable;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -217,6 +241,11 @@ void RimFlowPlotCollection::ensureDefaultFlowPlotsAreCreated()
m_defaultWellAllocOverTimePlot->setDescription( "Default Well Allocation Over Time Plot" );
}
if ( !m_defaultWellConnectivityTable() )
{
m_defaultWellConnectivityTable = new RimWellConnectivityTable;
}
if ( !m_flowCharacteristicsPlot() )
{
m_flowCharacteristicsPlot = new RimFlowCharacteristicsPlot;

View File

@@ -29,6 +29,7 @@ class RimWellAllocationPlot;
class RimFlowCharacteristicsPlot;
class RimWellDistributionPlot;
class RimWellDistributionPlotCollection;
class RimWellConnectivityTable;
//==================================================================================================
///
@@ -48,6 +49,7 @@ public:
void addWellAllocPlotToStoredPlots( RimWellAllocationPlot* plot );
void addFlowCharacteristicsPlotToStoredPlots( RimFlowCharacteristicsPlot* plot );
RimWellConnectivityTable* defaultWellConnectivityTable();
RimWellAllocationOverTimePlot* defaultWellAllocOverTimePlot();
RimWellAllocationPlot* defaultWellAllocPlot();
RimFlowCharacteristicsPlot* defaultFlowCharacteristicsPlot();
@@ -56,6 +58,7 @@ public:
private:
caf::PdmChildField<RimFlowCharacteristicsPlot*> m_flowCharacteristicsPlot;
caf::PdmChildField<RimWellConnectivityTable*> m_defaultWellConnectivityTable;
caf::PdmChildField<RimWellAllocationOverTimePlot*> m_defaultWellAllocOverTimePlot;
caf::PdmChildField<RimWellAllocationPlot*> m_defaultWellAllocPlot;
caf::PdmChildField<RimWellDistributionPlotCollection*> m_wellDistributionPlotCollection;

View File

@@ -1,339 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RimWellAllocationOverTimeCollection.h"
#include "cafAssert.h"
#include "RigAccWellFlowCalculator.h"
#include "RigFlowDiagResultAddress.h"
#include "RigWellResultPoint.h"
#include <set>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellAllocationOverTimeCollection::RimWellAllocationOverTimeCollection( const std::vector<QDateTime>& timeStepDates,
const std::map<QDateTime, RigAccWellFlowCalculator>& timeStepAndCalculatorPairs )
: m_timeStepDates( timeStepDates )
{
for ( const auto& [date, calculator] : timeStepAndCalculatorPairs )
{
std::string err = "Calculator for time step date " + date.toString().toStdString() + " does not exist in time step dates vector ";
CAF_ASSERT( std::find( m_timeStepDates.begin(), m_timeStepDates.end(), date ) != m_timeStepDates.end() && err.data() );
}
// Time steps not present in input map is considered "excluded" time steps
// Build new time step and calculator map using calculator for "next" valid time step for
// "excluded" time steps
QDateTime prevValidTimeStep;
for ( auto it = m_timeStepDates.rbegin(); it != m_timeStepDates.rend(); ++it )
{
const QDateTime& timeStep = *it;
auto timeStepCalculatorIt = timeStepAndCalculatorPairs.find( timeStep );
if ( timeStepCalculatorIt != timeStepAndCalculatorPairs.end() )
{
m_timeStepAndCalculatorPairs.emplace( timeStep, timeStepCalculatorIt->second );
prevValidTimeStep = timeStep;
}
else if ( prevValidTimeStep.isValid() )
{
// If no calculator for this time step, use the previous valid time step calculator
m_timeStepAndCalculatorPairs.emplace( timeStep, timeStepAndCalculatorPairs.at( prevValidTimeStep ) );
}
}
std::sort( m_timeStepDates.begin(), m_timeStepDates.end() );
// Retrieve union of well names across all calculators
std::set<QString> allWellNames;
for ( const auto& [date, calculator] : m_timeStepAndCalculatorPairs )
{
allWellNames.insert( calculator.tracerNames().begin(), calculator.tracerNames().end() );
}
// Fill default well values into map
const double defaultValue = 0.0;
for ( const auto& well : allWellNames )
{
for ( const auto& date : m_timeStepDates )
{
std::pair<QDateTime, double> defaultPair( date, defaultValue );
m_defaultWellValuesMap[well].insert( defaultPair );
}
}
m_wellValuesMap = m_defaultWellValuesMap;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellAllocationOverTimeCollection::fillWithFlowRatePercentageValues()
{
m_wellValuesMap = m_defaultWellValuesMap;
for ( auto& [timeStep, calculator] : m_timeStepAndCalculatorPairs )
{
const auto totalTracerFractions = calculator.totalTracerFractions();
for ( const auto& [wellName, value] : totalTracerFractions )
{
double valuePercent = 100.0 * value;
m_wellValuesMap[wellName][timeStep] = valuePercent;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellAllocationOverTimeCollection::fillWithFlowRateValues()
{
m_wellValuesMap = m_defaultWellValuesMap;
const size_t branchIdx = 0;
for ( auto& [timeStep, calculator] : m_timeStepAndCalculatorPairs )
{
for ( const auto& wellName : calculator.tracerNames() )
{
const auto& accumulatedConnectionFlows = calculator.accumulatedTracerFlowPrConnection( wellName, branchIdx );
const double topConnectionFlow = accumulatedConnectionFlows.empty() ? 0.0 : accumulatedConnectionFlows.back();
m_wellValuesMap[wellName][timeStep] = topConnectionFlow;
}
}
}
//--------------------------------------------------------------------------------------------------
/// Fill with flow volume at time step.
///
/// Create volume by multiplying with number of days since last time step.
//--------------------------------------------------------------------------------------------------
void RimWellAllocationOverTimeCollection::fillWithFlowVolumeValues()
{
fillWithFlowRateValues();
for ( auto& [well, timeStepsAndValues] : m_wellValuesMap )
{
QDateTime prevTimeStep;
for ( auto& [timeStep, value] : timeStepsAndValues )
{
if ( !prevTimeStep.isValid() )
{
prevTimeStep = timeStep;
continue;
}
const auto numDays = static_cast<double>( prevTimeStep.daysTo( timeStep ) );
value = value * numDays;
prevTimeStep = timeStep;
}
}
}
//--------------------------------------------------------------------------------------------------
/// Fill with accumulated flow volume over a range of time steps. Create volume by multiplying with
/// number of days since last time step.
///
/// Group small contributors in "Others" if accumulated volume value at last time step is below
/// threshold value.
//--------------------------------------------------------------------------------------------------
void RimWellAllocationOverTimeCollection::fillWithAccumulatedFlowVolumeValues( double smallContributionsThreshold )
{
fillWithFlowRateValues();
for ( auto& [well, timeStepsAndValues] : m_wellValuesMap )
{
QDateTime prevTimeStep;
double accumulatedVolume = 0.0;
for ( auto& [timeStep, value] : timeStepsAndValues )
{
if ( !prevTimeStep.isValid() )
{
prevTimeStep = timeStep;
continue;
}
const auto numDays = static_cast<double>( prevTimeStep.daysTo( timeStep ) );
const double volume = value * numDays;
accumulatedVolume += volume;
value = accumulatedVolume;
prevTimeStep = timeStep;
}
}
if ( smallContributionsThreshold > 0.0 )
{
groupAccumulatedFlowVolumes( m_wellValuesMap, smallContributionsThreshold );
}
}
//--------------------------------------------------------------------------------------------------
/// Fill with accumulated well flow volumes in percent of total accumulated flow volume at each
/// time step.
///
///
/// Group small contributors in "Others" if percentage value for well is below threshold at every
/// time step.
//--------------------------------------------------------------------------------------------------
void RimWellAllocationOverTimeCollection::fillWithAccumulatedFlowVolumePercentageValues( double smallContributionsThreshold )
{
// Handle threshold filtering afterwards
const double nonFilteringThreshold = 0.0;
fillWithAccumulatedFlowVolumeValues( nonFilteringThreshold );
for ( const auto& timeStep : m_timeStepDates )
{
double totalAccumulatedVolume = 0.0;
std::map<QString, double> timeStepWellValues;
// Sum accumulated volumes at time step
for ( auto& [well, values] : m_wellValuesMap )
{
const auto accumulatedVolume = values[timeStep];
totalAccumulatedVolume += accumulatedVolume;
timeStepWellValues[well] = accumulatedVolume;
}
// If no accumulated volume exist at time step
if ( totalAccumulatedVolume == 0.0 ) continue;
// Create percentage value
for ( auto& [well, value] : timeStepWellValues )
{
m_wellValuesMap[well][timeStep] = 100.0 * value / totalAccumulatedVolume;
}
}
if ( smallContributionsThreshold > 0.0 )
{
const auto percentageThreshold = 100.0 * smallContributionsThreshold;
groupAccumulatedFlowVolumePercentages( m_wellValuesMap, percentageThreshold );
}
}
//--------------------------------------------------------------------------------------------------
/// Handle grouping of small contributors in accumulated volume data based on threshold.
/// Group small contributors in "Others" if accumulated volume value at last time step is below
/// threshold value.
//--------------------------------------------------------------------------------------------------
void RimWellAllocationOverTimeCollection::groupAccumulatedFlowVolumes( std::map<QString, std::map<QDateTime, double>>& rWellValuesMap,
double threshold )
{
if ( m_timeStepDates.empty() ) return;
std::map<QString, std::map<QDateTime, double>> groupedWellValuesMap;
std::map<QString, double> lastAccumulatedWellValues;
double sumLastAccumulatedWellValues = 0.0;
const QDateTime lastTimeStep = m_timeStepDates.back();
for ( auto& [well, values] : rWellValuesMap )
{
const double lastWellValue = values[lastTimeStep];
lastAccumulatedWellValues[well] = lastWellValue;
sumLastAccumulatedWellValues += lastWellValue;
}
// Filter out wells with accumulated flow less than threshold and place among "others"
std::vector<QString> contributingWells;
std::vector<QString> groupedWells;
for ( const auto& [well, value] : lastAccumulatedWellValues )
{
if ( sumLastAccumulatedWellValues > 0.0 && ( value / sumLastAccumulatedWellValues ) < threshold )
{
groupedWells.push_back( well );
}
else
{
contributingWells.push_back( well );
}
}
for ( const auto& well : contributingWells )
{
groupedWellValuesMap[well] = rWellValuesMap[well];
}
for ( const auto& well : groupedWells )
{
if ( groupedWellValuesMap.count( RIG_TINY_TRACER_GROUP_NAME ) == 0 )
{
groupedWellValuesMap[RIG_TINY_TRACER_GROUP_NAME] = rWellValuesMap[well];
}
else
{
for ( const auto& [date, value] : rWellValuesMap[well] )
{
groupedWellValuesMap[RIG_TINY_TRACER_GROUP_NAME][date] += value;
}
}
}
rWellValuesMap = groupedWellValuesMap;
}
//--------------------------------------------------------------------------------------------------
/// Handle grouping of small contributors in accumulated volume percentage based on threshold.
/// Group small contributors in "Others" if percentage value for well is below threshold at every
/// time step. If percentage value is above threshold for one time step or more, show data for well
/// at every time step.
//--------------------------------------------------------------------------------------------------
void RimWellAllocationOverTimeCollection::groupAccumulatedFlowVolumePercentages( std::map<QString, std::map<QDateTime, double>>& rWellValuesMap,
double thresholdPercent )
{
auto getMaxValue = []( const std::map<QDateTime, double>& valuesMap ) -> double {
double maxValue = 0.0;
for ( const auto& [timeStep, value] : valuesMap )
{
maxValue = value > maxValue ? value : maxValue;
}
return maxValue;
};
std::vector<QString> contributingWells;
std::vector<QString> groupedWells;
for ( const auto& [well, values] : rWellValuesMap )
{
const double maxValue = getMaxValue( values );
if ( maxValue > thresholdPercent )
{
contributingWells.push_back( well );
}
else
{
groupedWells.push_back( well );
}
}
std::map<QString, std::map<QDateTime, double>> groupedWellValuesMap;
for ( const auto& well : contributingWells )
{
groupedWellValuesMap[well] = rWellValuesMap[well];
}
for ( const auto& well : groupedWells )
{
if ( groupedWellValuesMap.count( RIG_TINY_TRACER_GROUP_NAME ) == 0 )
{
groupedWellValuesMap[RIG_TINY_TRACER_GROUP_NAME] = rWellValuesMap[well];
}
else
{
for ( const auto& [date, value] : rWellValuesMap[well] )
{
groupedWellValuesMap[RIG_TINY_TRACER_GROUP_NAME][date] += value;
}
}
}
rWellValuesMap = groupedWellValuesMap;
}

View File

@@ -1,53 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include <QDateTime>
#include <QString>
#include <map>
#include <vector>
class RigAccWellFlowCalculator;
class RimWellAllocationOverTimeCollection
{
public:
RimWellAllocationOverTimeCollection( const std::vector<QDateTime>& timeStepDates,
const std::map<QDateTime, RigAccWellFlowCalculator>& timeStepAndCalculatorPairs );
const std::vector<QDateTime> timeStepDates() const { return m_timeStepDates; }
const std::map<QString, std::map<QDateTime, double>> wellValuesMap() const { return m_wellValuesMap; }
void fillWithFlowRatePercentageValues();
void fillWithFlowRateValues();
void fillWithFlowVolumeValues();
void fillWithAccumulatedFlowVolumeValues( double smallContributionsThreshold );
void fillWithAccumulatedFlowVolumePercentageValues( double smallContributionsThreshold );
private:
void groupAccumulatedFlowVolumes( std::map<QString, std::map<QDateTime, double>>& rWellValuesMap, double threshold );
void groupAccumulatedFlowVolumePercentages( std::map<QString, std::map<QDateTime, double>>& rWellValuesMap, double threshold );
private:
std::map<QDateTime, RigAccWellFlowCalculator> m_timeStepAndCalculatorPairs;
std::vector<QDateTime> m_timeStepDates;
std::map<QString, std::map<QDateTime, double>> m_defaultWellValuesMap;
std::map<QString, std::map<QDateTime, double>> m_wellValuesMap;
};

View File

@@ -28,6 +28,7 @@
#include "RigEclipseCaseData.h"
#include "RigSimWellData.h"
#include "RigSimulationWellCenterLineCalculator.h"
#include "RigWellAllocationOverTime.h"
#include "RigWellResultPoint.h"
#include "RimEclipseCaseTools.h"
@@ -37,7 +38,6 @@
#include "RimFlowDiagSolution.h"
#include "RimSimWellInView.h"
#include "RimStackablePlotCurve.h"
#include "RimWellAllocationOverTimeCollection.h"
#include "RimWellAllocationTools.h"
#include "RimWellLogFile.h"
#include "RimWellPlotTools.h"
@@ -108,7 +108,7 @@ RimWellAllocationOverTimePlot::RimWellAllocationOverTimePlot()
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, "TimeStepFilterMode", "Filter" );
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", "" );
@@ -186,23 +186,23 @@ RiuPlotWidget* RimWellAllocationOverTimePlot::plotWidget()
//--------------------------------------------------------------------------------------------------
QString RimWellAllocationOverTimePlot::asciiDataForPlotExport() const
{
// Retrieve collection of allocation over time data for wells
RimWellAllocationOverTimeCollection allocationOverTimeCollection = createWellAllocationOverTimeCollection();
// 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] : allocationOverTimeCollection.wellValuesMap() )
for ( auto& [wellName, wellValues] : allocationOverTime.wellValuesMap() )
{
dataText += wellName + "\t";
}
dataText += "\n";
const QString dateFormatStr = dateFormatString();
for ( const auto& timeStep : allocationOverTimeCollection.timeStepDates() )
for ( const auto& timeStep : allocationOverTime.timeStepDates() )
{
dataText += timeStep.toString( dateFormatStr ) + "\t";
for ( auto& [wellName, wellValues] : allocationOverTimeCollection.wellValuesMap() )
for ( auto& [wellName, wellValues] : allocationOverTime.wellValuesMap() )
{
dataText += wellValues.count( timeStep ) == 0 ? QString::number( 0.0 ) : QString::number( wellValues.at( timeStep ) );
dataText += "\t";
@@ -350,18 +350,18 @@ void RimWellAllocationOverTimePlot::updateFromWell()
}
m_plotWidget->detachItems( RiuPlotWidget::PlotItemType::CURVE );
// Retrieve collection of total fraction data for wells
RimWellAllocationOverTimeCollection allocationOverTimeCollection = createWellAllocationOverTimeCollection();
std::vector<double> allStackedValues( allocationOverTimeCollection.timeStepDates().size(), 0.0 );
// Retrieve total fraction data for wells
RigWellAllocationOverTime allocationOverTime = createWellAllocationOverTime();
std::vector<double> allStackedValues( allocationOverTime.timeStepDates().size(), 0.0 );
// Negative z-position to show grid lines
int zPos = -10000;
for ( auto& [wellName, wellValues] : allocationOverTimeCollection.wellValuesMap() )
for ( auto& [wellName, wellValues] : allocationOverTime.wellValuesMap() )
{
cvf::Color3f color = m_flowDiagSolution ? m_flowDiagSolution->tracerColor( wellName ) : getTracerColor( wellName );
for ( size_t i = 0; i < allocationOverTimeCollection.timeStepDates().size(); ++i )
for ( size_t i = 0; i < allocationOverTime.timeStepDates().size(); ++i )
{
const auto value = wellValues.at( allocationOverTimeCollection.timeStepDates()[i] );
const auto value = wellValues.at( allocationOverTime.timeStepDates()[i] );
allStackedValues[i] += value;
}
@@ -375,7 +375,7 @@ void RimWellAllocationOverTimePlot::updateFromWell()
RiuPlotCurve* curve = m_plotWidget->createPlotCurve( nullptr, wellName );
curve->setAppearance( RiuQwtPlotCurveDefines::LineStyleEnum::STYLE_SOLID, interpolationType, 2, qColor, fillBrush );
curve->setSamplesFromDatesAndYValues( allocationOverTimeCollection.timeStepDates(), allStackedValues, false );
curve->setSamplesFromDatesAndYValues( allocationOverTime.timeStepDates(), allStackedValues, false );
curve->attachToPlot( m_plotWidget );
curve->showInPlot();
curve->setZ( zPos-- );
@@ -412,23 +412,23 @@ void RimWellAllocationOverTimePlot::updateFromWell()
/// well data for all time steps. If well does not exist for specific time step date - value is
/// set to 0.
//--------------------------------------------------------------------------------------------------
RimWellAllocationOverTimeCollection RimWellAllocationOverTimePlot::createWellAllocationOverTimeCollection() const
RigWellAllocationOverTime RimWellAllocationOverTimePlot::createWellAllocationOverTime() const
{
if ( !m_case )
{
return RimWellAllocationOverTimeCollection( {}, {} );
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 RimWellAllocationOverTimeCollection( {}, {} );
return RigWellAllocationOverTime( {}, {} );
}
const RigSimWellData* simWellData = m_case->eclipseCaseData()->findSimWellData( m_wellName );
if ( !simWellData )
{
return RimWellAllocationOverTimeCollection( {}, {} );
return RigWellAllocationOverTime( {}, {} );
}
// Note: Threshold per calculator does not work for accumulated data - use no threshold for each calculator
@@ -470,7 +470,8 @@ RimWellAllocationOverTimeCollection RimWellAllocationOverTimePlot::createWellAll
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 ) );
m_case->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ),
nullptr );
const auto calculator = RigAccWellFlowCalculator( pipeBranchesCLCoords,
pipeBranchesCellIds,
tracerFractionCellValues,
@@ -491,41 +492,41 @@ RimWellAllocationOverTimeCollection RimWellAllocationOverTimePlot::createWellAll
}
}
// Create collection
const auto selectedTimeStepsVector = std::vector( selectedTimeSteps.begin(), selectedTimeSteps.end() );
RimWellAllocationOverTimeCollection collection( selectedTimeStepsVector, timeStepAndCalculatorPairs );
// 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 )
{
collection.fillWithFlowRatePercentageValues();
wellAllocationOverTime.fillWithFlowRatePercentageValues();
}
else if ( m_flowValueType == FlowValueType::FLOW_RATE )
{
collection.fillWithFlowRateValues();
wellAllocationOverTime.fillWithFlowRateValues();
}
else if ( m_flowValueType == FlowValueType::FLOW_VOLUME )
{
collection.fillWithFlowVolumeValues();
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;
collection.fillWithAccumulatedFlowVolumeValues( actualSmallContributionThreshold );
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;
collection.fillWithAccumulatedFlowVolumePercentageValues( actualSmallContributionThreshold );
wellAllocationOverTime.fillWithAccumulatedFlowVolumePercentageValues( actualSmallContributionThreshold );
}
else
{
CAF_ASSERT( "Not handled FlowValue type!" );
}
return collection;
return wellAllocationOverTime;
}
//--------------------------------------------------------------------------------------------------
@@ -782,8 +783,8 @@ QString RimWellAllocationOverTimePlot::dateFormatString() const
//--------------------------------------------------------------------------------------------------
/// 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 set the 10 first time steps
/// for case. If less than 10 time steps exist, all are selected.
/// If both selected time steps exist for case, keep as is, otherwise select first and last time
/// step in case.
//--------------------------------------------------------------------------------------------------
void RimWellAllocationOverTimePlot::setValidTimeStepRangeForCase()
{
@@ -821,77 +822,14 @@ int RimWellAllocationOverTimePlot::axisValueFontSize() const
return caf::FontTools::absolutePointSize( RiaPreferences::current()->defaultPlotFontSize(), m_axisValueFontSize() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QDateTime> RimWellAllocationOverTimePlot::getTimeStepsWithinSelectedRange( const std::vector<QDateTime>& timeSteps ) const
{
std::vector<QDateTime> selectedTimeSteps;
auto isTimeStepInSelectedRange = [&]( const QDateTime& timeStep ) -> bool {
return m_selectedFromTimeStep() <= timeStep && timeStep <= m_selectedToTimeStep();
};
std::copy_if( timeSteps.begin(), timeSteps.end(), std::back_inserter( selectedTimeSteps ), isTimeStepInSelectedRange );
return selectedTimeSteps;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<QDateTime> RimWellAllocationOverTimePlot::getSelectedTimeSteps( const std::vector<QDateTime>& timeSteps ) const
{
const auto timeStepsInRange = getTimeStepsWithinSelectedRange( timeSteps );
return m_timeStepFilterMode == TimeStepFilterMode::TIME_STEP_COUNT ? createEvenlyDistributedDates( timeStepsInRange, m_timeStepCount )
: std::set( timeStepsInRange.begin(), timeStepsInRange.end() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<QDateTime> RimWellAllocationOverTimePlot::createEvenlyDistributedDates( const std::vector<QDateTime>& inputDates, int numDates )
{
std::set<QDateTime> outputDates;
if ( inputDates.empty() || numDates <= 0 )
{
return outputDates;
}
if ( static_cast<size_t>( numDates ) > inputDates.size() )
{
outputDates = std::set( inputDates.begin(), inputDates.end() );
return outputDates;
}
if ( numDates == 1 )
{
outputDates = { inputDates.front() };
return outputDates;
}
// Find the minimum and maximum dates in the input vector
QDateTime minDate = *std::min_element( inputDates.begin(), inputDates.end() );
QDateTime maxDate = *std::max_element( inputDates.begin(), inputDates.end() );
// Calculate the time step between each selected date
qint64 timeStep = ( maxDate.toMSecsSinceEpoch() - minDate.toMSecsSinceEpoch() ) / ( static_cast<qint64>( numDates ) - 1 );
// Find the index of the input date that is closest to each new date
for ( int i = 0; i < numDates; ++i )
{
qint64 targetTime = minDate.toMSecsSinceEpoch() + i * timeStep;
int closestIndex = 0;
qint64 closestTimeDiff = std::numeric_limits<qint64>::max();
for ( size_t j = 0; j < inputDates.size(); ++j )
{
qint64 timeDiff = std::abs( inputDates[j].toMSecsSinceEpoch() - targetTime );
if ( timeDiff < closestTimeDiff )
{
closestIndex = j;
closestTimeDiff = timeDiff;
}
}
// Add the closest date to the output vector
outputDates.insert( inputDates[closestIndex] );
}
return outputDates;
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() );
}

View File

@@ -33,7 +33,7 @@
class RigAccWellFlowCalculator;
class RimEclipseResultCase;
class RimFlowDiagSolution;
class RimWellAllocationOverTimeCollection;
class RigWellAllocationOverTime;
class RimSimWellInView;
class RiuPlotWidget;
class RiuQwtPlotWidget;
@@ -101,10 +101,10 @@ private:
private:
void doUpdateLayout() override;
void updateFromWell();
RimWellAllocationOverTimeCollection createWellAllocationOverTimeCollection() const;
std::set<QString> findSortedWellNames();
cvf::Color3f getTracerColor( const QString& tracerName );
void updateFromWell();
RigWellAllocationOverTime createWellAllocationOverTime() const;
std::set<QString> findSortedWellNames();
cvf::Color3f getTracerColor( const QString& tracerName );
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
@@ -118,9 +118,7 @@ private:
int axisTitleFontSize() const;
int axisValueFontSize() const;
std::vector<QDateTime> getTimeStepsWithinSelectedRange( const std::vector<QDateTime>& timeSteps ) const;
std::set<QDateTime> getSelectedTimeSteps( const std::vector<QDateTime>& timeSteps ) const;
static std::set<QDateTime> createEvenlyDistributedDates( const std::vector<QDateTime>& inputDates, int numDates );
std::set<QDateTime> getSelectedTimeSteps( const std::vector<QDateTime>& timeSteps ) const;
private:
caf::PdmField<QString> m_userName;

View File

@@ -266,7 +266,8 @@ void RimWellAllocationPlot::updateFromWell()
bool isProducer = ( simWellData->wellProductionType( m_timeStep ) == RiaDefines::WellProductionType::PRODUCER ||
simWellData->wellProductionType( m_timeStep ) == RiaDefines::WellProductionType::UNDEFINED_PRODUCTION_TYPE );
RigEclCellIndexCalculator cellIdxCalc( m_case->eclipseCaseData()->mainGrid(),
m_case->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ) );
m_case->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ),
nullptr );
wfCalculator.reset( new RigAccWellFlowCalculator( pipeBranchesCLCoords,
pipeBranchesCellIds,
tracerFractionCellValues,

View File

@@ -20,16 +20,30 @@
#include "RiaDefines.h"
#include "RigFlowDiagResultAddress.h"
#include "RigFlowDiagResults.h"
#include "RigSimWellData.h"
#include "RimFlowDiagSolution.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::map<QString, const std::vector<double>*>
RimWellAllocationTools::findOrCreateRelevantTracerCellFractions( const RigSimWellData* simWellData,
RimFlowDiagSolution* flowDiagSolution,
int timeStepIndex )
{
return findOrCreateRelevantTracerCellFractions( simWellData, flowDiagSolution, RigFlowDiagResultAddress::PHASE_ALL, timeStepIndex );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::map<QString, const std::vector<double>*>
RimWellAllocationTools::findOrCreateRelevantTracerCellFractions( const RigSimWellData* simWellData,
RimFlowDiagSolution* flowDiagSolution,
RigFlowDiagResultAddress::PhaseSelection phaseSelection,
int timeStepIndex )
{
std::map<QString, const std::vector<double>*> tracerCellFractionValues = {};
if ( flowDiagSolution && simWellData->hasWellResult( timeStepIndex ) )
@@ -51,7 +65,7 @@ std::map<QString, const std::vector<double>*>
{
if ( flowDiagSolution->tracerStatusInTimeStep( tracerName, timeStepIndex ) == requestedTracerType )
{
RigFlowDiagResultAddress resAddr( RIG_FLD_CELL_FRACTION_RESNAME, RigFlowDiagResultAddress::PHASE_ALL, tracerName.toStdString() );
RigFlowDiagResultAddress resAddr( RIG_FLD_CELL_FRACTION_RESNAME, phaseSelection, tracerName.toStdString() );
const std::vector<double>* tracerCellFractions = flowDiagSolution->flowDiagResults()->resultValues( resAddr, timeStepIndex );
if ( tracerCellFractions )
{

View File

@@ -18,6 +18,8 @@
#pragma once
#include "RigFlowDiagResultAddress.h"
#include <QString>
#include <map>
#include <vector>
@@ -32,4 +34,9 @@ namespace RimWellAllocationTools
{
std::map<QString, const std::vector<double>*>
findOrCreateRelevantTracerCellFractions( const RigSimWellData* simWellData, RimFlowDiagSolution* flowDiagSolution, int timeStepIndex );
}
std::map<QString, const std::vector<double>*> findOrCreateRelevantTracerCellFractions( const RigSimWellData* simWellData,
RimFlowDiagSolution* flowDiagSolution,
RigFlowDiagResultAddress::PhaseSelection phaseSelection,
int timeStepIndex );
} // namespace RimWellAllocationTools

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,183 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "RimPlotWindow.h"
#include "RigFlowDiagResultAddress.h"
#include "cafPdmField.h"
#include "cafPdmPtrField.h"
#include <QDateTime>
#include <QPointer>
#include <QString>
class RimEclipseResultCase;
class RimEclipseView;
class RimFlowDiagSolution;
class RimRegularLegendConfig;
class RimSimWellInView;
class RigWellAllocationOverTime;
class RiuMatrixPlotWidget;
class RigSimWellData;
class RigAccWellFlowCalculator;
//==================================================================================================
///
//==================================================================================================
class RimWellConnectivityTable : public RimPlotWindow
{
CAF_PDM_HEADER_INIT;
public:
enum class TimeStepSelection
{
SINGLE_TIME_STEP,
TIME_STEP_RANGE,
};
enum class TimeSampleValueType
{
FLOW_RATE,
FLOW_RATE_FRACTION,
FLOW_RATE_PERCENTAGE,
};
enum class TimeRangeValueType
{
ACCUMULATED_FLOW_VOLUME,
ACCUMULATED_FLOW_VOLUME_FRACTION,
ACCUMULATED_FLOW_VOLUME_PERCENTAGE,
};
enum class TimeStepRangeFilterMode
{
NONE,
TIME_STEP_COUNT,
};
public:
RimWellConnectivityTable();
~RimWellConnectivityTable() override;
void setFromSimulationWell( RimSimWellInView* simWell );
private:
void cleanupBeforeClose();
void onLoadDataAndUpdate() override;
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
void childFieldChangedByUi( const caf::PdmFieldHandle* changedChildField ) override;
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override;
// Inherited via RimPlotWindow
virtual QString description() const override;
virtual void doRenderWindowContent( QPaintDevice* paintDevice ) override;
// Inherited via RimViewWindow
virtual QWidget* viewWidget() override;
virtual QImage snapshotWindowContent() override;
virtual void zoomAll() override;
virtual QWidget* createViewWidget( QWidget* mainWindowParent ) override;
virtual void deleteViewWidget() override;
int axisTitleFontSize() const;
int axisLabelFontSize() const;
int valueLabelFontSize() const;
private:
std::map<QString, RigWellAllocationOverTime>
createProductionWellsAllocationOverTimeMap( const std::set<QString>& selectedProductionWells ) const;
RigWellAllocationOverTime createWellAllocationOverTime( const RigSimWellData* simWellData ) const;
void createAndEmplaceTimeStepAndCalculatorPairInMap( std::map<QDateTime, RigAccWellFlowCalculator>& rTimeStepAndCalculatorPairs,
const QDateTime timeStep,
int timeStepIndex,
const RigSimWellData* simWellData ) const;
std::set<QDateTime> getSelectedTimeSteps( const std::vector<QDateTime>& timeSteps ) const;
QString dateFormatString() const;
std::vector<QString> getProductionWellNames() const;
std::vector<QString> getProductionWellNamesAtTimeSteps( const std::set<QDateTime>& timeSteps ) const;
QString createTableTitle() const;
std::pair<double, double> createLegendMinMaxValues( const double maxTableValue ) const;
void setValidTimeStepSelectionsForCase();
void setValidSingleTimeStepForCase();
void setValidTimeStepRangeForCase();
bool isTimeStepInCase( const QDateTime& timeStep ) const;
int getTimeStepIndex( const QDateTime timeStep, const std::vector<QDateTime> timeSteps ) const;
void setSelectedProducersAndInjectorsForSingleTimeStep();
void setSelectedProducersAndInjectorsForTimeStepRange();
void syncSelectedInjectorsFromProducerSelection();
void syncSelectedProducersFromInjectorSelection();
void onCellFiltersChanged( const SignalEmitter* emitter );
void connectViewCellFiltersChangedToSlot( RimEclipseView* view );
void disconnectViewCellFiltersChangedFromSlots( RimEclipseView* view );
private:
// Matrix plot for visualizing table data
QPointer<RiuMatrixPlotWidget> m_matrixPlotWidget;
caf::PdmPtrField<RimEclipseResultCase*> m_case;
caf::PdmPtrField<RimEclipseView*> m_cellFilterView;
caf::PdmPtrField<RimFlowDiagSolution*> m_flowDiagSolution;
caf::PdmField<caf::AppEnum<TimeStepSelection>> m_timeStepSelection;
caf::PdmField<caf::AppEnum<TimeSampleValueType>> m_timeSampleValueType;
caf::PdmField<caf::AppEnum<TimeRangeValueType>> m_timeRangeValueType;
caf::PdmField<bool> m_selectProducersAndInjectorsForTimeSteps;
caf::PdmField<double> m_thresholdValue;
// For single time sample
caf::PdmField<QDateTime> m_selectedTimeStep;
// For time step range
caf::PdmField<QDateTime> m_selectedFromTimeStep;
caf::PdmField<QDateTime> m_selectedToTimeStep;
caf::PdmField<caf::AppEnum<TimeStepRangeFilterMode>> m_timeStepFilterMode;
caf::PdmField<int> m_timeStepCount;
caf::PdmField<std::vector<QDateTime>> m_excludeTimeSteps;
caf::PdmField<bool> m_applyTimeStepSelections;
caf::PdmChildField<RimRegularLegendConfig*> m_legendConfig;
caf::PdmField<std::vector<QString>> m_selectedInjectorTracersUiField;
caf::PdmField<std::vector<QString>> m_selectedProducerTracersUiField;
caf::PdmField<bool> m_syncSelectedProducersFromInjectorSelection;
caf::PdmField<bool> m_syncSelectedInjectorsFromProducerSelection;
caf::PdmField<bool> m_applySelectedInectorProducerTracers;
caf::PdmField<caf::FontTools::RelativeSizeEnum> m_axisTitleFontSize;
caf::PdmField<caf::FontTools::RelativeSizeEnum> m_axisLabelFontSize;
caf::PdmField<caf::FontTools::RelativeSizeEnum> m_valueLabelFontSize;
caf::PdmField<bool> m_showValueLabels;
const int m_initialNumberOfTimeSteps = 10;
};

View File

@@ -54,6 +54,7 @@
#include "RimEclipseResultCase.h"
#include "RimEclipseView.h"
#include "RimFlowDiagSolution.h"
#include "RimFlowDiagnosticsTools.h"
#include "RimGridCrossPlot.h"
#include "RimGridCrossPlotDataSet.h"
#include "RimGridTimeHistoryCurve.h"
@@ -753,11 +754,13 @@ QList<caf::PdmOptionItemInfo> RimEclipseResultDefinition::calculateValueOptions(
}
else if ( fieldNeedingOptions == &m_selectedInjectorTracersUiField )
{
options = calcOptionsForSelectedTracerField( true );
const bool isInjector = true;
options = RimFlowDiagnosticsTools::calcOptionsForSelectedTracerField( m_flowSolutionUiField(), isInjector );
}
else if ( fieldNeedingOptions == &m_selectedProducerTracersUiField )
{
options = calcOptionsForSelectedTracerField( false );
const bool isInjector = false;
options = RimFlowDiagnosticsTools::calcOptionsForSelectedTracerField( m_flowSolutionUiField(), isInjector );
}
}
else if ( m_resultTypeUiField() == RiaDefines::ResultCatType::INJECTION_FLOODING )
@@ -1644,25 +1647,6 @@ void RimEclipseResultDefinition::defineEditorAttribute( const caf::PdmFieldHandl
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEclipseResultDefinition::TracerComp::operator()( const QString& lhs, const QString& rhs ) const
{
if ( !lhs.endsWith( "-XF" ) && rhs.endsWith( "-XF" ) )
{
return true;
}
else if ( lhs.endsWith( "-XF" ) && !rhs.endsWith( "-XF" ) )
{
return false;
}
else
{
return lhs < rhs;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -2243,36 +2227,6 @@ void RimEclipseResultDefinition::updateLegendTitle( RimRegularLegendConfig* lege
legendConfig->setTitle( title );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<caf::PdmOptionItemInfo> RimEclipseResultDefinition::calcOptionsForSelectedTracerField( bool injector )
{
QList<caf::PdmOptionItemInfo> options;
RimFlowDiagSolution* flowSol = m_flowSolutionUiField();
if ( flowSol )
{
std::set<QString, TracerComp> sortedTracers = setOfTracersOfType( injector );
for ( const QString& tracerName : sortedTracers )
{
QString postfix;
RimFlowDiagSolution::TracerStatusType status = flowSol->tracerStatusOverall( tracerName );
if ( status == RimFlowDiagSolution::TracerStatusType::VARYING )
{
postfix = " [I/P]";
}
else if ( status == RimFlowDiagSolution::TracerStatusType::UNDEFINED )
{
postfix = " [U]";
}
options.push_back( caf::PdmOptionItemInfo( tracerName + postfix, tracerName ) );
}
}
return options;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -2418,50 +2372,6 @@ QStringList RimEclipseResultDefinition::getResultNamesForResultType( RiaDefines:
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QString> RimEclipseResultDefinition::allTracerNames() const
{
std::vector<QString> tracerNames;
RimFlowDiagSolution* flowSol = m_flowSolutionUiField();
if ( flowSol )
{
tracerNames = flowSol->tracerNames();
}
return tracerNames;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<QString, RimEclipseResultDefinition::TracerComp> RimEclipseResultDefinition::setOfTracersOfType( bool injector ) const
{
std::set<QString, TracerComp> sortedTracers;
RimFlowDiagSolution* flowSol = m_flowSolutionUiField();
if ( flowSol )
{
std::vector<QString> tracerNames = allTracerNames();
for ( const QString& tracerName : tracerNames )
{
RimFlowDiagSolution::TracerStatusType status = flowSol->tracerStatusOverall( tracerName );
bool includeTracer = status == RimFlowDiagSolution::TracerStatusType::VARYING ||
status == RimFlowDiagSolution::TracerStatusType::UNDEFINED;
includeTracer |= injector && status == RimFlowDiagSolution::TracerStatusType::INJECTOR;
includeTracer |= !injector && status == RimFlowDiagSolution::TracerStatusType::PRODUCER;
if ( includeTracer )
{
sortedTracers.insert( tracerName );
}
}
}
return sortedTracers;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -2473,7 +2383,8 @@ RimEclipseResultDefinition::FlowTracerSelectionState RimEclipseResultDefinition:
}
else if ( m_flowTracerSelectionMode == FLOW_TR_BY_SELECTION )
{
if ( m_selectedInjectorTracers().size() == setOfTracersOfType( true ).size() )
const bool isInjector = true;
if ( m_selectedInjectorTracers().size() == RimFlowDiagnosticsTools::setOfTracersOfType( m_flowSolutionUiField(), isInjector ).size() )
{
return ALL_SELECTED;
}
@@ -2500,7 +2411,8 @@ RimEclipseResultDefinition::FlowTracerSelectionState RimEclipseResultDefinition:
}
else if ( m_flowTracerSelectionMode == FLOW_TR_BY_SELECTION )
{
if ( m_selectedProducerTracers().size() == setOfTracersOfType( false ).size() )
const bool isInjector = false;
if ( m_selectedProducerTracers().size() == RimFlowDiagnosticsTools::setOfTracersOfType( m_flowSolutionUiField(), isInjector ).size() )
{
return ALL_SELECTED;
}
@@ -2521,8 +2433,6 @@ RimEclipseResultDefinition::FlowTracerSelectionState RimEclipseResultDefinition:
//--------------------------------------------------------------------------------------------------
void RimEclipseResultDefinition::syncInjectorToProducerSelection()
{
const double epsilon = 1.0e-8;
int timeStep = 0;
Rim3dView* rimView = nullptr;
@@ -2535,21 +2445,8 @@ void RimEclipseResultDefinition::syncInjectorToProducerSelection()
RimFlowDiagSolution* flowSol = m_flowSolution();
if ( flowSol && m_flowTracerSelectionMode == FLOW_TR_BY_SELECTION )
{
std::set<QString, TracerComp> producers = setOfTracersOfType( false );
std::set<QString, TracerComp> newProducerSelection;
for ( const QString& selectedInjector : m_selectedInjectorTracers() )
{
for ( const QString& producer : producers )
{
std::pair<double, double> commFluxes =
flowSol->flowDiagResults()->injectorProducerPairFluxes( selectedInjector.toStdString(), producer.toStdString(), timeStep );
if ( std::abs( commFluxes.first ) > epsilon || std::abs( commFluxes.second ) > epsilon )
{
newProducerSelection.insert( producer );
}
}
}
std::set<QString, RimFlowDiagnosticsTools::TracerComp> newProducerSelection =
RimFlowDiagnosticsTools::setOfProducerTracersFromInjectors( m_flowSolutionUiField(), m_selectedInjectorTracers(), timeStep );
// Add all currently selected producers to set
for ( const QString& selectedProducer : m_selectedProducerTracers() )
{
@@ -2565,8 +2462,6 @@ void RimEclipseResultDefinition::syncInjectorToProducerSelection()
//--------------------------------------------------------------------------------------------------
void RimEclipseResultDefinition::syncProducerToInjectorSelection()
{
const double epsilon = 1.0e-8;
int timeStep = 0;
Rim3dView* rimView = nullptr;
@@ -2579,21 +2474,9 @@ void RimEclipseResultDefinition::syncProducerToInjectorSelection()
RimFlowDiagSolution* flowSol = m_flowSolution();
if ( flowSol && m_flowTracerSelectionMode == FLOW_TR_BY_SELECTION )
{
std::set<QString, TracerComp> injectors = setOfTracersOfType( true );
std::set<QString, RimFlowDiagnosticsTools::TracerComp> newInjectorSelection =
RimFlowDiagnosticsTools::setOfInjectorTracersFromProducers( m_flowSolutionUiField(), m_selectedProducerTracers(), timeStep );
std::set<QString, TracerComp> newInjectorSelection;
for ( const QString& selectedProducer : m_selectedProducerTracers() )
{
for ( const QString& injector : injectors )
{
std::pair<double, double> commFluxes =
flowSol->flowDiagResults()->injectorProducerPairFluxes( injector.toStdString(), selectedProducer.toStdString(), timeStep );
if ( std::abs( commFluxes.first ) > epsilon || std::abs( commFluxes.second ) > epsilon )
{
newInjectorSelection.insert( injector );
}
}
}
// Add all currently selected injectors to set
for ( const QString& selectedInjector : m_selectedInjectorTracers() )
{

View File

@@ -188,11 +188,6 @@ protected:
caf::PdmPointer<RimEclipseCase> m_eclipseCase;
private:
struct TracerComp
{
bool operator()( const QString& lhs, const QString& rhs ) const;
};
caf::PdmField<int> m_timeLapseBaseTimestep;
caf::PdmPtrField<RimEclipseCase*> m_differenceCase;
caf::PdmField<bool> m_divideByCellFaceArea;
@@ -202,8 +197,6 @@ private:
QString flowDiagResUiText( bool shortLabel, int maxTracerStringLength = std::numeric_limits<int>::max() ) const;
QList<caf::PdmOptionItemInfo> calcOptionsForSelectedTracerField( bool injector );
QString timeOfFlightString( bool shorter ) const;
QString maxFractionTracerString( bool shorter ) const;
@@ -212,9 +205,6 @@ private:
void changedTracerSelectionField( bool injector );
static QStringList getResultNamesForResultType( RiaDefines::ResultCatType resultCatType, const RigCaseCellResultsData* results );
std::vector<QString> allTracerNames() const;
std::set<QString, TracerComp> setOfTracersOfType( bool injector ) const;
FlowTracerSelectionState injectorSelectionState() const;
FlowTracerSelectionState producerSelectionState() const;

View File

@@ -103,6 +103,7 @@ void RimRegularLegendConfig::ColorRangeEnum::setUp()
addItem( RimRegularLegendConfig::ColorRangesType::GREEN_RED, "GREEN_RED", "Green to Red" );
addItem( RimRegularLegendConfig::ColorRangesType::BLUE_MAGENTA, "BLUE_MAGENTA", "Blue to Magenta" );
addItem( RimRegularLegendConfig::ColorRangesType::CORRELATION, "CORRELATION", "Correlation colors" );
addItem( RimRegularLegendConfig::ColorRangesType::HEAT_MAP, "HEAT_MAP", "Heat map colors" );
addItem( RimRegularLegendConfig::ColorRangesType::UNDEFINED, "UNDEFINED", "Undefined" );
setDefault( RimRegularLegendConfig::ColorRangesType::UNDEFINED );
}
@@ -392,6 +393,7 @@ void RimRegularLegendConfig::updateLegend()
{
m_significantDigitsInData = m_precision;
updateTickCountAndUserDefinedRange();
if ( m_resetUserDefinedValues && m_globalAutoMax != cvf::UNDEFINED_DOUBLE )
{
updateTickCountAndUserDefinedRange();
@@ -1112,6 +1114,9 @@ cvf::Color3ubArray RimRegularLegendConfig::colorArrayFromColorType( ColorRangesT
case RimRegularLegendConfig::ColorRangesType::CORRELATION:
return RiaColorTables::correlationPaletteColors().color3ubArray();
break;
case RimRegularLegendConfig::ColorRangesType::HEAT_MAP:
return RiaColorTables::heatMapPaletteColors().color3ubArray();
break;
default:
if ( ColorManager::isEnsembleColorRange( colorType ) ) return ColorManager::EnsembleColorRanges().at( colorType );
break;
@@ -1229,6 +1234,13 @@ void RimRegularLegendConfig::defineUiOrdering( QString uiConfigName, caf::PdmUiO
uiOrdering.add( &m_colorLegend );
uiOrdering.skipRemainingFields( true );
}
else if ( uiConfigName == "FlagColorsAndMappingModeOnly" )
{
uiOrdering.add( &m_showLegend );
uiOrdering.add( &m_colorLegend );
uiOrdering.add( &m_mappingMode );
uiOrdering.skipRemainingFields( true );
}
else
{
caf::PdmUiOrdering* formatGr = uiOrdering.addNewGroup( "Format" );

View File

@@ -97,6 +97,8 @@ public:
CORRELATION,
HEAT_MAP,
UNDEFINED
};