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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 2692 additions and 294 deletions

View File

@ -32,6 +32,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RiaScheduler.h
${CMAKE_CURRENT_LIST_DIR}/RiaSummaryDefines.h
${CMAKE_CURRENT_LIST_DIR}/RiaLasDefines.h
${CMAKE_CURRENT_LIST_DIR}/RiaWellFlowDefines.h
)
set(SOURCE_GROUP_SOURCE_FILES
@ -68,6 +69,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RiaScheduler.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaSummaryDefines.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaLasDefines.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaWellFlowDefines.cpp
)
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})

View File

@ -0,0 +1,27 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RiaWellFlowDefines.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiaDefines::reservoirTracerName()
{
return "Reservoir";
}

View File

@ -0,0 +1,26 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 <QString>
namespace RiaDefines
{
QString reservoirTracerName();
}; // namespace RiaDefines

View File

@ -531,6 +531,24 @@ const caf::ColorTable& RiaColorTables::correlationPaletteColors()
return colorTable;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const caf::ColorTable& RiaColorTables::heatMapPaletteColors()
{
static std::vector<cvf::Color3ub> colors{ cvf::Color3ub::DARK_BLUE,
cvf::Color3ub( 0, 0, 240 ), // Medium Blue
cvf::Color3ub( 0, 102, 204 ), // Transition Medium Blue to Cyan
cvf::Color3ub::CYAN,
cvf::Color3ub( 75, 255, 47 ), // Green/Yellow more green
cvf::Color3ub::DARK_ORANGE,
cvf::Color3ub::YELLOW };
static caf::ColorTable colorTable = caf::ColorTable( colors );
return colorTable;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -62,6 +62,7 @@ public:
static const caf::ColorTable& wellPathsPaletteColors();
static const caf::ColorTable& waterAndRockPaletteColors();
static const caf::ColorTable& correlationPaletteColors();
static const caf::ColorTable& heatMapPaletteColors();
static cvf::Color3f undefinedCellColor();

View File

@ -499,3 +499,68 @@ QList<caf::PdmOptionItemInfo> RiaQDateTimeTools::createOptionItems( const std::v
return options;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<QDateTime> RiaQDateTimeTools::createEvenlyDistributedDates( const std::vector<QDateTime>& inputDates, int numDates )
{
std::set<QDateTime> outputDates;
if ( inputDates.empty() || numDates <= 0 )
{
return {};
}
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;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QDateTime> RiaQDateTimeTools::getTimeStepsWithinSelectedRange( const std::vector<QDateTime>& timeSteps,
const QDateTime& fromTimeStep,
const QDateTime& toTimeStep )
{
std::vector<QDateTime> selectedTimeSteps;
auto isTimeStepInSelectedRange = [&]( const QDateTime& timeStep ) -> bool { return fromTimeStep <= timeStep && timeStep <= toTimeStep; };
std::copy_if( timeSteps.begin(), timeSteps.end(), std::back_inserter( selectedTimeSteps ), isTimeStepInSelectedRange );
return selectedTimeSteps;
}

View File

@ -24,6 +24,7 @@
#include <QString>
#include <set>
#include <string>
#include <vector>
@ -93,6 +94,10 @@ public:
static QList<caf::PdmOptionItemInfo> createOptionItems( const std::vector<time_t>& timeSteps );
static std::set<QDateTime> createEvenlyDistributedDates( const std::vector<QDateTime>& inputDates, int numDates );
static std::vector<QDateTime>
getTimeStepsWithinSelectedRange( const std::vector<QDateTime>& timeSteps, const QDateTime& fromTimeStep, const QDateTime& toTimeStep );
private:
static const DateTimeSpan TIMESPAN_DAY;
static const DateTimeSpan TIMESPAN_WEEK;

View File

@ -29,6 +29,7 @@
#include "RimSimWellInViewCollection.h"
#include "RimWellAllocationOverTimePlot.h"
#include "RimWellAllocationPlot.h"
#include "RimWellConnectivityTable.h"
#include "RimWellPath.h"
#include "RiuPlotMainWindowTools.h"
@ -111,6 +112,8 @@ void RicShowWellAllocationPlotFeature::onActionTriggered( bool isChecked )
flowPlotColl->defaultWellAllocOverTimePlot()->setFromSimulationWell( simWell );
flowPlotColl->defaultWellAllocOverTimePlot()->updateConnectedEditors();
flowPlotColl->defaultWellConnectivityTable()->setFromSimulationWell( simWell );
RiuPlotMainWindowTools::showPlotMainWindow();
RiuPlotMainWindowTools::onObjectAppended( flowPlotColl->defaultWellAllocOverTimePlot() );
RiuPlotMainWindowTools::onObjectAppended( flowPlotColl->defaultWellAllocPlot() );

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

@ -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
};

View File

@ -88,6 +88,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RigWellLogIndexDepthOffset.h
${CMAKE_CURRENT_LIST_DIR}/RigPressureDepthData.h
${CMAKE_CURRENT_LIST_DIR}/RigMswCenterLineCalculator.h
${CMAKE_CURRENT_LIST_DIR}/RigWellAllocationOverTime.h
)
set(SOURCE_GROUP_SOURCE_FILES
@ -173,6 +174,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RigWellLogIndexDepthOffset.cpp
${CMAKE_CURRENT_LIST_DIR}/RigPressureDepthData.cpp
${CMAKE_CURRENT_LIST_DIR}/RigMswCenterLineCalculator.cpp
${CMAKE_CURRENT_LIST_DIR}/RigWellAllocationOverTime.cpp
)
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})

View File

@ -18,6 +18,8 @@
#include "RigAccWellFlowCalculator.h"
#include "RiaWellFlowDefines.h"
#include "RigActiveCellInfo.h"
#include "RigFlowDiagResults.h"
#include "RigMainGrid.h"
@ -37,12 +39,23 @@
//--------------------------------------------------------------------------------------------------
size_t RigEclCellIndexCalculator::resultCellIndex( size_t gridIndex, size_t gridCellIndex ) const
{
const RigGridBase* grid = m_mainGrid->gridByIndex( gridIndex );
size_t reservoirCellIndex = grid->reservoirCellIndex( gridCellIndex );
size_t reservoirCellIndex = m_mainGrid->reservoirCellIndexByGridAndGridLocalCellIndex( gridIndex, gridCellIndex );
return m_activeCellInfo->cellResultIndex( reservoirCellIndex );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RigEclCellIndexCalculator::isCellVisible( size_t gridIndex, size_t gridCellIndex ) const
{
if ( !m_cellVisibilities ) return true;
size_t reservoirCellIndex = m_mainGrid->reservoirCellIndexByGridAndGridLocalCellIndex( gridIndex, gridCellIndex );
return ( *m_cellVisibilities )[reservoirCellIndex];
}
//==================================================================================================
///
///
@ -73,7 +86,7 @@ RigAccWellFlowCalculator::RigAccWellFlowCalculator( const std::vector<std::vecto
for ( const auto& it : ( *m_tracerCellFractionValues ) )
m_tracerNames.push_back( it.first );
m_tracerNames.push_back( RIG_RESERVOIR_TRACER_NAME );
m_tracerNames.push_back( RiaDefines::reservoirTracerName() );
initializePipeBranchesMeasuredDepths();
calculateFlowData();
@ -90,7 +103,7 @@ RigAccWellFlowCalculator::RigAccWellFlowCalculator( const std::vector<std::vecto
: m_pipeBranchesCLCoords( pipeBranchesCLCoords )
, m_pipeBranchesWellResultPoints( pipeBranchesWellResultPoints )
, m_tracerCellFractionValues( nullptr )
, m_cellIndexCalculator( RigEclCellIndexCalculator( nullptr, nullptr ) )
, m_cellIndexCalculator( RigEclCellIndexCalculator( nullptr, nullptr, nullptr ) )
, m_smallContributionsThreshold( smallContribThreshold )
, m_isProducer( true )
, m_useTotalWellPhaseRateOnly( false )
@ -123,7 +136,7 @@ RigAccWellFlowCalculator::RigAccWellFlowCalculator( const std::vector<cvf::Vec3d
const std::vector<double>& pipeBranchMeasuredDepths,
bool totalFlowOnly )
: m_tracerCellFractionValues( nullptr )
, m_cellIndexCalculator( RigEclCellIndexCalculator( nullptr, nullptr ) )
, m_cellIndexCalculator( RigEclCellIndexCalculator( nullptr, nullptr, nullptr ) )
, m_smallContributionsThreshold( 0.0 )
, m_isProducer( true )
, m_useTotalWellPhaseRateOnly( totalFlowOnly )
@ -697,6 +710,8 @@ std::vector<double> RigAccWellFlowCalculator::calculateWellCellFlowPrTracer( con
{
std::vector<double> flowPrTracer( m_tracerNames.size(), 0.0 );
if ( wellCell.isCell() && !m_cellIndexCalculator.isCellVisible( wellCell.gridIndex(), wellCell.cellIndex() ) ) return flowPrTracer;
if ( !isConnectionFlowConsistent( wellCell ) )
{
double flowRate = wellCell.flowRate();
@ -866,12 +881,16 @@ void RigAccWellFlowCalculator::groupSmallContributions()
for ( const auto& tracerPair : totalTracerFractions )
{
if ( fabs( tracerPair.second ) <= m_smallContributionsThreshold &&
( hasConsistentWellFlow || tracerPair.first != RIG_RESERVOIR_TRACER_NAME ) ) // Do not group the
// Reservoir tracer if the
// well flow is
// inconsistent, because
// cross flow is shown as
// the reservoir fraction
( hasConsistentWellFlow || tracerPair.first != RiaDefines::reservoirTracerName() ) ) // Do not group
// the Reservoir
// tracer if the
// well flow is
// inconsistent,
// because cross
// flow is shown
// as the
// reservoir
// fraction
{
tracersToGroup.push_back( tracerPair.first );
}

View File

@ -18,6 +18,8 @@
#pragma once
#include <cvfArray.h>
#include <cstddef>
#include <map>
#include <vector>
@ -33,17 +35,20 @@ class RigActiveCellInfo;
class RigEclCellIndexCalculator
{
public:
RigEclCellIndexCalculator( const RigMainGrid* mainGrid, const RigActiveCellInfo* activeCellInfo )
RigEclCellIndexCalculator( const RigMainGrid* mainGrid, const RigActiveCellInfo* activeCellInfo, const cvf::UByteArray* cellVisibilities )
: m_mainGrid( mainGrid )
, m_activeCellInfo( activeCellInfo )
, m_cellVisibilities( cellVisibilities )
{
}
size_t resultCellIndex( size_t gridIndex, size_t gridCellIndex ) const;
bool isCellVisible( size_t gridIndex, size_t gridCellIndex ) const;
private:
const RigMainGrid* m_mainGrid;
const RigActiveCellInfo* m_activeCellInfo;
const cvf::UByteArray* m_cellVisibilities;
};
//==================================================================================================
@ -125,6 +130,9 @@ private:
bool m_isProducer;
bool m_useTotalWellPhaseRateOnly;
cvf::UByteArray* m_cellVisibilities = nullptr;
RigMainGrid* m_mainGrid = nullptr;
struct BranchFlow
{
std::vector<double> depthValuesFromTop;

View File

@ -16,7 +16,7 @@
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimWellAllocationOverTimeCollection.h"
#include "RigWellAllocationOverTime.h"
#include "cafAssert.h"
@ -29,8 +29,8 @@
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellAllocationOverTimeCollection::RimWellAllocationOverTimeCollection( const std::vector<QDateTime>& timeStepDates,
const std::map<QDateTime, RigAccWellFlowCalculator>& timeStepAndCalculatorPairs )
RigWellAllocationOverTime::RigWellAllocationOverTime( const std::vector<QDateTime>& timeStepDates,
const std::map<QDateTime, RigAccWellFlowCalculator>& timeStepAndCalculatorPairs )
: m_timeStepDates( timeStepDates )
{
for ( const auto& [date, calculator] : timeStepAndCalculatorPairs )
@ -84,7 +84,23 @@ RimWellAllocationOverTimeCollection::RimWellAllocationOverTimeCollection( const
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellAllocationOverTimeCollection::fillWithFlowRatePercentageValues()
void RigWellAllocationOverTime::fillWithFlowRateFractionValues()
{
m_wellValuesMap = m_defaultWellValuesMap;
for ( auto& [timeStep, calculator] : m_timeStepAndCalculatorPairs )
{
const auto totalTracerFractions = calculator.totalTracerFractions();
for ( const auto& [wellName, value] : totalTracerFractions )
{
m_wellValuesMap[wellName][timeStep] = value;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigWellAllocationOverTime::fillWithFlowRatePercentageValues()
{
m_wellValuesMap = m_defaultWellValuesMap;
for ( auto& [timeStep, calculator] : m_timeStepAndCalculatorPairs )
@ -101,7 +117,7 @@ void RimWellAllocationOverTimeCollection::fillWithFlowRatePercentageValues()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellAllocationOverTimeCollection::fillWithFlowRateValues()
void RigWellAllocationOverTime::fillWithFlowRateValues()
{
m_wellValuesMap = m_defaultWellValuesMap;
const size_t branchIdx = 0;
@ -121,7 +137,7 @@ void RimWellAllocationOverTimeCollection::fillWithFlowRateValues()
///
/// Create volume by multiplying with number of days since last time step.
//--------------------------------------------------------------------------------------------------
void RimWellAllocationOverTimeCollection::fillWithFlowVolumeValues()
void RigWellAllocationOverTime::fillWithFlowVolumeValues()
{
fillWithFlowRateValues();
@ -150,7 +166,7 @@ void RimWellAllocationOverTimeCollection::fillWithFlowVolumeValues()
/// Group small contributors in "Others" if accumulated volume value at last time step is below
/// threshold value.
//--------------------------------------------------------------------------------------------------
void RimWellAllocationOverTimeCollection::fillWithAccumulatedFlowVolumeValues( double smallContributionsThreshold )
void RigWellAllocationOverTime::fillWithAccumulatedFlowVolumeValues( double smallContributionsThreshold )
{
fillWithFlowRateValues();
@ -181,15 +197,34 @@ void RimWellAllocationOverTimeCollection::fillWithAccumulatedFlowVolumeValues( d
}
//--------------------------------------------------------------------------------------------------
/// Fill with accumulated well flow volumes in percent of total accumulated flow volume at each
/// time step.
///
//--------------------------------------------------------------------------------------------------
void RigWellAllocationOverTime::fillWithAccumulatedFlowVolumeFractionValues( double smallContributionsThreshold )
{
fillWithAccumulatedFlowVolumeFractionOrPercentageValues( FractionOrPercentage::FRACTION, smallContributionsThreshold );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigWellAllocationOverTime::fillWithAccumulatedFlowVolumePercentageValues( double smallContributionsThreshold )
{
fillWithAccumulatedFlowVolumeFractionOrPercentageValues( FractionOrPercentage::PERCENTAGE, smallContributionsThreshold );
}
//--------------------------------------------------------------------------------------------------
/// Fill with accumulated well flow volumes in fraction/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
/// Group small contributors in "Others" if volume value for well is below threshold at every
/// time step.
//--------------------------------------------------------------------------------------------------
void RimWellAllocationOverTimeCollection::fillWithAccumulatedFlowVolumePercentageValues( double smallContributionsThreshold )
void RigWellAllocationOverTime::fillWithAccumulatedFlowVolumeFractionOrPercentageValues( FractionOrPercentage selection,
double smallContributionsThreshold )
{
const double scaling = selection == FractionOrPercentage::FRACTION ? 1.0 : 100.0;
// Handle threshold filtering afterwards
const double nonFilteringThreshold = 0.0;
fillWithAccumulatedFlowVolumeValues( nonFilteringThreshold );
@ -213,14 +248,14 @@ void RimWellAllocationOverTimeCollection::fillWithAccumulatedFlowVolumePercentag
// Create percentage value
for ( auto& [well, value] : timeStepWellValues )
{
m_wellValuesMap[well][timeStep] = 100.0 * value / totalAccumulatedVolume;
m_wellValuesMap[well][timeStep] = scaling * value / totalAccumulatedVolume;
}
}
if ( smallContributionsThreshold > 0.0 )
{
const auto percentageThreshold = 100.0 * smallContributionsThreshold;
groupAccumulatedFlowVolumePercentages( m_wellValuesMap, percentageThreshold );
const auto threshold = scaling * smallContributionsThreshold;
groupAccumulatedFlowVolumeFractionsOrPercentages( m_wellValuesMap, threshold );
}
}
@ -229,8 +264,7 @@ void RimWellAllocationOverTimeCollection::fillWithAccumulatedFlowVolumePercentag
/// 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 )
void RigWellAllocationOverTime::groupAccumulatedFlowVolumes( std::map<QString, std::map<QDateTime, double>>& rWellValuesMap, double threshold )
{
if ( m_timeStepDates.empty() ) return;
@ -283,13 +317,13 @@ void RimWellAllocationOverTimeCollection::groupAccumulatedFlowVolumes( std::map<
}
//--------------------------------------------------------------------------------------------------
/// 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
/// Handle grouping of small contributors in accumulated volume fraction/percentage based on threshold.
/// Group small contributors in "Others" if fraction/percentage value for well is below threshold at every
/// time step. If fraction/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 )
void RigWellAllocationOverTime::groupAccumulatedFlowVolumeFractionsOrPercentages( std::map<QString, std::map<QDateTime, double>>& rWellValuesMap,
double threshold )
{
auto getMaxValue = []( const std::map<QDateTime, double>& valuesMap ) -> double {
double maxValue = 0.0;
@ -305,7 +339,7 @@ void RimWellAllocationOverTimeCollection::groupAccumulatedFlowVolumePercentages(
for ( const auto& [well, values] : rWellValuesMap )
{
const double maxValue = getMaxValue( values );
if ( maxValue > thresholdPercent )
if ( maxValue > threshold )
{
contributingWells.push_back( well );
}

View File

@ -26,24 +26,34 @@
class RigAccWellFlowCalculator;
class RimWellAllocationOverTimeCollection
class RigWellAllocationOverTime
{
public:
RimWellAllocationOverTimeCollection( const std::vector<QDateTime>& timeStepDates,
const std::map<QDateTime, RigAccWellFlowCalculator>& timeStepAndCalculatorPairs );
RigWellAllocationOverTime( 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 fillWithFlowRateFractionValues();
void fillWithFlowRatePercentageValues();
void fillWithFlowRateValues();
void fillWithFlowVolumeValues();
void fillWithAccumulatedFlowVolumeValues( double smallContributionsThreshold );
void fillWithAccumulatedFlowVolumeFractionValues( double smallContributionsThreshold );
void fillWithAccumulatedFlowVolumePercentageValues( double smallContributionsThreshold );
private:
enum class FractionOrPercentage
{
FRACTION,
PERCENTAGE
};
void fillWithAccumulatedFlowVolumeFractionOrPercentageValues( FractionOrPercentage selection, double smallContributionsThreshold );
void groupAccumulatedFlowVolumes( std::map<QString, std::map<QDateTime, double>>& rWellValuesMap, double threshold );
void groupAccumulatedFlowVolumePercentages( std::map<QString, std::map<QDateTime, double>>& rWellValuesMap, double threshold );
void groupAccumulatedFlowVolumeFractionsOrPercentages( std::map<QString, std::map<QDateTime, double>>& rWellValuesMap, double threshold );
private:
std::map<QDateTime, RigAccWellFlowCalculator> m_timeStepAndCalculatorPairs;

View File

@ -105,6 +105,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RiuGuiTheme.h
${CMAKE_CURRENT_LIST_DIR}/RiuQssSyntaxHighlighter.h
${CMAKE_CURRENT_LIST_DIR}/RiuQwtDateScaleWrapper.h
${CMAKE_CURRENT_LIST_DIR}/RiuMatrixPlotWidget.h
)
set(SOURCE_GROUP_SOURCE_FILES
@ -211,6 +212,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RiuTextContentFrame.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuQwtLegendOverlayContentFrame.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuQwtDateScaleWrapper.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuMatrixPlotWidget.cpp
)
if(RESINSIGHT_USE_QT_CHARTS)
@ -301,6 +303,7 @@ list(
${CMAKE_CURRENT_LIST_DIR}/RiuTextEditWithCompletion.h
${CMAKE_CURRENT_LIST_DIR}/RiuTextContentFrame.h
${CMAKE_CURRENT_LIST_DIR}/RiuQwtLegendOverlayContentFrame.h
${CMAKE_CURRENT_LIST_DIR}/RiuMatrixPlotWidget.h
)
list(APPEND QT_UI_FILES)

View File

@ -0,0 +1,443 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RiuMatrixPlotWidget.h"
#include "RiaColorTools.h"
#include "RiaPreferences.h"
#include "RimRegularLegendConfig.h"
#include "RimViewWindow.h"
#include "RiuAbstractLegendFrame.h"
#include "RiuQwtLinearScaleEngine.h"
#include "RiuQwtPlotItem.h"
#include "RiuQwtPlotTools.h"
#include "RiuScalarMapperLegendFrame.h"
#include "cvfColor3.h"
#include "qwt_plot_marker.h"
#include "qwt_scale_draw.h"
#include "qwt_text.h"
#include <QHBoxLayout>
class MatrixShapeItem : public QwtPlotShapeItem
{
public:
MatrixShapeItem( const QString& title = QString() )
: QwtPlotShapeItem( title )
{
}
public:
double value;
size_t rowIndex;
size_t columnIndex;
};
class TextScaleDraw : public QwtScaleDraw
{
public:
TextScaleDraw( const std::map<size_t, QString>& tickLabels )
: m_tickLabels( tickLabels )
{
}
QwtText label( double value ) const override
{
size_t intValue = static_cast<size_t>( value + 0.25 );
auto it = m_tickLabels.find( intValue );
return it != m_tickLabels.end() ? it->second : "";
}
private:
std::map<size_t, QString> m_tickLabels;
};
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuMatrixPlotWidget::RiuMatrixPlotWidget( RimViewWindow* ownerViewWindow, RimRegularLegendConfig* legendConfig, QWidget* parent )
: matrixCellSelected( this )
, m_ownerViewWindow( ownerViewWindow )
, m_legendConfig( legendConfig )
{
// Configure main layout
QHBoxLayout* mainLayout = new QHBoxLayout();
mainLayout->setContentsMargins( 15, 15, 15, 15 );
this->setLayout( mainLayout );
// White background
QPalette palette = this->palette();
palette.setColor( QPalette::Window, Qt::white );
this->setAutoFillBackground( true );
this->setPalette( palette );
// Add plot to main layout
m_plotWidget = new RiuQwtPlotWidget( nullptr, parent );
m_plotWidget->qwtPlot()->insertLegend( nullptr );
mainLayout->addWidget( m_plotWidget );
// Add legend to main layout
if ( m_legendConfig )
{
m_legendFrame = m_legendConfig->makeLegendFrame();
mainLayout->addWidget( m_legendFrame );
}
// Configure plot widget to be a matrix plot?
m_plotWidget->enableGridLines( RiuPlotAxis::defaultTop(), false, false );
m_plotWidget->enableGridLines( RiuPlotAxis::defaultBottom(), false, false );
m_plotWidget->enableGridLines( RiuPlotAxis::defaultRight(), false, false );
m_plotWidget->enableGridLines( RiuPlotAxis::defaultLeft(), false, false );
m_plotWidget->setAxisTitleEnabled( RiuPlotAxis::defaultLeft(), true );
m_plotWidget->setAxisTitleEnabled( RiuPlotAxis::defaultBottom(), true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuMatrixPlotWidget::~RiuMatrixPlotWidget()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QwtPlot* RiuMatrixPlotWidget::qwtPlot() const
{
return m_plotWidget->qwtPlot();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::clearPlotData()
{
m_columnHeaders = {};
m_rowHeaders = {};
m_rowValues = {};
m_plotWidget->qwtPlot()->detachItems();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setColumnHeaders( const std::vector<QString>& columnHeaders )
{
if ( m_columnHeaders.empty() )
{
m_columnHeaders = columnHeaders;
}
else if ( columnHeaders.size() == m_columnHeaders.size() )
{
m_columnHeaders = columnHeaders;
}
CAF_ASSERT( "Column headers must be assigned for an empty matrix or re-assigned with an equal number of columns!" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setRowValues( const QString& rowLabel, const std::vector<double>& values )
{
CAF_ASSERT( !m_columnHeaders.empty() && "Matrix column headers are not configured - headers are empty!" );
CAF_ASSERT( values.size() == m_columnHeaders.size() && "Number of row values must be equal number of configured matrix columns" );
// Insert in front to get rows from bottom to top in plot
m_rowHeaders.insert( m_rowHeaders.begin(), rowLabel );
m_rowValues.insert( m_rowValues.begin(), values );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::createPlot()
{
updateAxes();
createMatrixCells();
scheduleReplot();
auto frame = dynamic_cast<RiuScalarMapperLegendFrame*>( m_legendFrame.data() );
frame->updateTickValues();
frame->update();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::scheduleReplot()
{
m_plotWidget->scheduleReplot();
}
RimViewWindow* RiuMatrixPlotWidget::ownerViewWindow() const
{
return m_ownerViewWindow;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setPlotTitleEnabled( bool enabled )
{
m_plotWidget->setPlotTitleEnabled( enabled );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setLegendFontSize( int fontSize )
{
m_plotWidget->setLegendFontSize( fontSize );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setPlotTitle( const QString& title )
{
m_plotWidget->setPlotTitle( title );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setShowValueLabel( bool showValueLabel )
{
m_showValueLabel = showValueLabel;
// Due to few data points - clear plot and create matrix cells with new label flag
m_plotWidget->qwtPlot()->detachItems();
createMatrixCells();
m_plotWidget->scheduleReplot();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setRowTitle( const QString& title )
{
m_rowTitle = title;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setInvalidValueColor( const cvf::Color3ub& color )
{
m_invalidValueColor = color;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setUseInvalidValueColor( bool useInvalidValueColor )
{
m_useInvalidValueColor = useInvalidValueColor;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setInvalidValueRange( double min, double max )
{
CAF_ASSERT( min <= max && "Min must be less or equal to max!" );
m_invalidValueRange = std::make_pair( min, max );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setColumnTitle( const QString& title )
{
m_columnTitle = title;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setPlotTitleFontSize( int fontSize )
{
m_plotWidget->setPlotTitleFontSize( fontSize );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setAxisTitleFontSize( int fontSize )
{
m_axisTitleFontSize = fontSize;
m_plotWidget->setAxisFontsAndAlignment( RiuPlotAxis::defaultLeft(), m_axisTitleFontSize, m_axisLabelFontSize );
m_plotWidget->setAxisFontsAndAlignment( RiuPlotAxis::defaultBottom(), m_axisTitleFontSize, m_axisLabelFontSize );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setAxisLabelFontSize( int fontSize )
{
m_axisLabelFontSize = fontSize;
m_plotWidget->setAxisFontsAndAlignment( RiuPlotAxis::defaultLeft(), m_axisTitleFontSize, m_axisLabelFontSize );
m_plotWidget->setAxisFontsAndAlignment( RiuPlotAxis::defaultBottom(), m_axisTitleFontSize, m_axisLabelFontSize );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::setValueFontSize( int fontSize )
{
m_valueFontSize = fontSize;
// Due to few data points - clear plot and create matrix cells with new font size
m_plotWidget->qwtPlot()->detachItems();
createMatrixCells();
m_plotWidget->scheduleReplot();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::updateAxes()
{
if ( !m_plotWidget ) return;
// Labels on y-axis
m_plotWidget->qwtPlot()->setAxisScaleDraw( QwtAxis::YLeft, new TextScaleDraw( createIndexLabelMap( m_rowHeaders ) ) );
m_plotWidget->qwtPlot()->setAxisScaleEngine( QwtAxis::YLeft, new RiuQwtLinearScaleEngine );
m_plotWidget->setAxisTitleText( RiuPlotAxis::defaultLeft(), m_rowTitle );
m_plotWidget->setAxisTitleEnabled( RiuPlotAxis::defaultLeft(), true );
m_plotWidget->setAxisFontsAndAlignment( RiuPlotAxis::defaultLeft(), m_axisTitleFontSize, m_axisLabelFontSize, false, Qt::AlignCenter );
m_plotWidget->setAxisLabelsAndTicksEnabled( RiuPlotAxis::defaultLeft(), true, false );
m_plotWidget->setAxisRange( RiuPlotAxis::defaultLeft(), 0.0, static_cast<double>( m_rowHeaders.size() ) + 1 );
m_plotWidget->setMajorAndMinorTickIntervalsAndRange( RiuPlotAxis::defaultLeft(),
1.0,
0.0,
0.5,
static_cast<double>( m_rowHeaders.size() ) - 0.5,
0.0,
static_cast<double>( m_rowHeaders.size() ) );
// Labels on column axis
auto scaleDraw = new TextScaleDraw( createIndexLabelMap( m_columnHeaders ) );
scaleDraw->setLabelRotation( 30.0 );
m_plotWidget->qwtPlot()->setAxisScaleDraw( QwtAxis::XBottom, scaleDraw );
m_plotWidget->qwtPlot()->setAxisScaleEngine( QwtAxis::XBottom, new RiuQwtLinearScaleEngine );
m_plotWidget->setAxisTitleText( RiuPlotAxis::defaultBottom(), m_columnTitle );
m_plotWidget->setAxisTitleEnabled( RiuPlotAxis::defaultBottom(), true );
m_plotWidget->setAxisFontsAndAlignment( RiuPlotAxis::defaultBottom(),
m_axisTitleFontSize,
m_axisLabelFontSize,
false,
Qt::AlignCenter | Qt::AlignTop );
m_plotWidget->setAxisLabelsAndTicksEnabled( RiuPlotAxis::defaultBottom(), true, false );
m_plotWidget->setAxisRange( RiuPlotAxis::defaultBottom(), 0.0, static_cast<double>( m_columnHeaders.size() ) + 1 );
m_plotWidget->setMajorAndMinorTickIntervalsAndRange( RiuPlotAxis::defaultBottom(),
1.0,
0.0,
0.5,
static_cast<double>( m_columnHeaders.size() ) - 0.5,
0.0,
static_cast<double>( m_columnHeaders.size() ) );
m_plotWidget->qwtPlot()->setAxisLabelAlignment( QwtAxis::XBottom, Qt::AlignRight );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::createMatrixCells()
{
CAF_ASSERT( m_legendConfig.notNull() && m_legendConfig->scalarMapper() && "Scalar mapper must be set for legend config!" );
for ( size_t rowIdx = 0u; rowIdx < m_rowValues.size(); ++rowIdx )
{
for ( size_t colIdx = 0u; colIdx < m_rowValues[rowIdx].size(); ++colIdx )
{
const double value = m_rowValues[rowIdx][colIdx];
const auto label = QString( "%1" ).arg( value, 0, 'f', 2 );
cvf::Color3ub color = m_legendConfig->scalarMapper()->mapToColor( value );
if ( m_useInvalidValueColor && m_invalidValueRange.first <= value && value <= m_invalidValueRange.second )
{
color = m_invalidValueColor;
}
QColor qColor( color.r(), color.g(), color.b() );
auto rectangle = RiuQwtPlotTools::createBoxShapeT<MatrixShapeItem>( label,
static_cast<double>( colIdx ),
static_cast<double>( colIdx ) + 1.0,
static_cast<double>( rowIdx ),
static_cast<double>( rowIdx ) + 1,
qColor );
rectangle->value = value;
rectangle->rowIndex = rowIdx;
rectangle->columnIndex = colIdx;
rectangle->attach( m_plotWidget->qwtPlot() );
if ( m_showValueLabel )
{
QwtText textLabel( label );
cvf::Color3f contrastColor = RiaColorTools::contrastColor( cvf::Color3f( color ) );
textLabel.setColor( RiaColorTools::toQColor( contrastColor ) );
QFont font = textLabel.font();
font.setPixelSize( caf::FontTools::pointSizeToPixelSize( m_valueFontSize ) );
textLabel.setFont( font );
QwtPlotMarker* marker = new QwtPlotMarker();
marker->setLabel( textLabel );
marker->setXValue( colIdx + 0.5 );
marker->setYValue( rowIdx + 0.5 );
marker->attach( m_plotWidget->qwtPlot() );
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::map<size_t, QString> RiuMatrixPlotWidget::createIndexLabelMap( const std::vector<QString>& labels )
{
std::map<size_t, QString> indexLabelMap;
for ( size_t i = 0; i < labels.size(); ++i )
{
indexLabelMap.emplace( i, labels[i] );
}
return indexLabelMap;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMatrixPlotWidget::onPlotItemSelected( std::shared_ptr<RiuPlotItem> plotItem, bool toggle, int sampleIndex )
{
RiuQwtPlotItem* qwtPlotItem = dynamic_cast<RiuQwtPlotItem*>( plotItem.get() );
if ( !qwtPlotItem ) return;
MatrixShapeItem* matrixItem = dynamic_cast<MatrixShapeItem*>( qwtPlotItem->qwtPlotItem() );
if ( matrixItem )
{
matrixCellSelected.send( std::make_pair( matrixItem->rowIndex, matrixItem->columnIndex ) );
}
}

View File

@ -0,0 +1,110 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RiuInterfaceToViewWindow.h"
#include "RiuPlotItem.h"
#include "RiuQwtPlotWidget.h"
#include "cafFontTools.h"
#include "cafPdmPointer.h"
#include "cafSignal.h"
#include "cvfScalarMapper.h"
#include "qwt_plot.h"
#include <QPointer>
#include <QWidget>
class RimViewWindow;
class RimRegularLegendConfig;
class RiuAbstractLegendFrame;
class RiuMatrixPlotWidget : public QWidget, public RiuInterfaceToViewWindow, public caf::SignalEmitter
{
Q_OBJECT
public:
caf::Signal<std::pair<int, int>> matrixCellSelected;
public:
RiuMatrixPlotWidget( RimViewWindow* ownerViewWindow, RimRegularLegendConfig* legendConfig, QWidget* parent = nullptr );
~RiuMatrixPlotWidget();
QwtPlot* qwtPlot() const;
void createPlot();
void clearPlotData();
void setColumnHeaders( const std::vector<QString>& columnHeaders );
void setRowValues( const QString& rowLabel, const std::vector<double>& values );
void setPlotTitle( const QString& title );
void setColumnTitle( const QString& title );
void setRowTitle( const QString& title );
void setInvalidValueColor( const cvf::Color3ub& color );
void setUseInvalidValueColor( bool useInvalidValueColor );
void setInvalidValueRange( double min, double max );
void setShowValueLabel( bool showValueLabel );
void setPlotTitleFontSize( int fontSize );
void setPlotTitleEnabled( bool enabled );
void setLegendFontSize( int fontSize );
void setAxisTitleFontSize( int fontSize );
void setAxisLabelFontSize( int fontSize );
void setValueFontSize( int fontSize );
void scheduleReplot();
virtual RimViewWindow* ownerViewWindow() const override;
private slots:
void onPlotItemSelected( std::shared_ptr<RiuPlotItem> plotItem, bool toggle, int sampleIndex );
private:
void updateAxes();
void createMatrixCells();
std::map<size_t, QString> createIndexLabelMap( const std::vector<QString>& labels );
private:
QPointer<RiuQwtPlotWidget> m_plotWidget;
QPointer<RiuAbstractLegendFrame> m_legendFrame;
caf::PdmPointer<RimViewWindow> m_ownerViewWindow; // Only intended to be used by ownerViewWindow()
caf::PdmPointer<RimRegularLegendConfig> m_legendConfig;
std::vector<QString> m_columnHeaders;
std::vector<QString> m_rowHeaders;
std::vector<std::vector<double>> m_rowValues;
cvf::Color3ub m_invalidValueColor = cvf::Color3ub::WHITE;
bool m_useInvalidValueColor = false;
std::pair<double, double> m_invalidValueRange = { 0.0, 0.0 };
bool m_showValueLabel = true;
QString m_rowTitle;
QString m_columnTitle;
int m_axisTitleFontSize = 8;
int m_axisLabelFontSize = 8;
int m_valueFontSize = 8;
};

View File

@ -39,10 +39,7 @@ RiuScalarMapperLegendFrame::RiuScalarMapperLegendFrame( QWidget* parent, const Q
, m_tickNumberPrecision( 4 )
, m_numberFormat( RiaNumberFormat::NumberFormatType::AUTO )
{
if ( m_scalarMapper.notNull() )
{
m_scalarMapper->majorTickValues( &m_tickValues );
}
updateTickValues();
}
//--------------------------------------------------------------------------------------------------
@ -68,6 +65,18 @@ void RiuScalarMapperLegendFrame::setTickFormat( NumberFormat format )
m_numberFormat = format;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuScalarMapperLegendFrame::updateTickValues()
{
if ( m_scalarMapper.notNull() )
{
m_tickValues = {};
m_scalarMapper->majorTickValues( &m_tickValues );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -46,6 +46,7 @@ public:
void setTickPrecision( int precision );
void setTickFormat( NumberFormat format );
void updateTickValues();
private:
void layoutInfo( LayoutInfo* layout ) const override;