mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
Well allocation over time plot (#9655)
- Plot for showing well allocation over time. - Select time range - Option to exclude time steps in selected range - Possible value types: Flow rate, flow rate percentage, flow volume, accumulated flow volume, accumulated flow volume percentage - Group small contributors into group "Others"
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
#include "RimSummaryCrossPlot.h"
|
||||
#include "RimSummaryPlot.h"
|
||||
#include "RimVfpPlot.h"
|
||||
#include "RimWellAllocationOverTimePlot.h"
|
||||
#include "RimWellLogPlot.h"
|
||||
#include "RimWellLogTrack.h"
|
||||
|
||||
@@ -189,7 +190,7 @@ bool RicShowPlotDataFeature::isCommandEnabled()
|
||||
|
||||
if ( dynamic_cast<RimSummaryPlot*>( plot ) || dynamic_cast<RimWellLogPlot*>( plot ) ||
|
||||
dynamic_cast<RimWellLogTrack*>( plot ) || dynamic_cast<RimGridCrossPlot*>( plot ) ||
|
||||
dynamic_cast<RimVfpPlot*>( plot ) )
|
||||
dynamic_cast<RimVfpPlot*>( plot ) || dynamic_cast<RimWellAllocationOverTimePlot*>( plot ) )
|
||||
{
|
||||
validPlots++;
|
||||
}
|
||||
@@ -221,11 +222,12 @@ void RicShowPlotDataFeature::onActionTriggered( bool isChecked )
|
||||
std::vector<RimPlotWindow*> selection;
|
||||
getSelection( selection );
|
||||
|
||||
std::vector<RimSummaryPlot*> selectedSummaryPlots;
|
||||
std::vector<RimWellLogPlot*> wellLogPlots;
|
||||
std::vector<RimGridCrossPlot*> crossPlots;
|
||||
std::vector<RimVfpPlot*> vfpPlots;
|
||||
std::vector<RimWellLogTrack*> depthTracks;
|
||||
std::vector<RimSummaryPlot*> selectedSummaryPlots;
|
||||
std::vector<RimWellLogPlot*> wellLogPlots;
|
||||
std::vector<RimGridCrossPlot*> crossPlots;
|
||||
std::vector<RimVfpPlot*> vfpPlots;
|
||||
std::vector<RimWellLogTrack*> depthTracks;
|
||||
std::vector<RimWellAllocationOverTimePlot*> wellAllocationOverTimePlots;
|
||||
|
||||
for ( auto plot : selection )
|
||||
{
|
||||
@@ -258,6 +260,12 @@ void RicShowPlotDataFeature::onActionTriggered( bool isChecked )
|
||||
depthTracks.push_back( depthTrack );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( auto wellAllocationOverTimePlot = dynamic_cast<RimWellAllocationOverTimePlot*>( plot ) )
|
||||
{
|
||||
wellAllocationOverTimePlots.push_back( wellAllocationOverTimePlot );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for ( RimSummaryPlot* summaryPlot : selectedSummaryPlots )
|
||||
@@ -292,6 +300,13 @@ void RicShowPlotDataFeature::onActionTriggered( bool isChecked )
|
||||
auto textProvider = new RiuTabbedGridCrossPlotTextProvider( crossPlot );
|
||||
RicShowPlotDataFeature::showTabbedTextWindow( textProvider );
|
||||
}
|
||||
|
||||
for ( RimWellAllocationOverTimePlot* wellAllocationOverTimePlot : wellAllocationOverTimePlots )
|
||||
{
|
||||
QString title = wellAllocationOverTimePlot->description();
|
||||
QString text = wellAllocationOverTimePlot->asciiDataForPlotExport();
|
||||
RicShowPlotDataFeature::showTextWindow( title, text );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "RimMainPlotCollection.h"
|
||||
#include "RimSimWellInView.h"
|
||||
#include "RimSimWellInViewCollection.h"
|
||||
#include "RimWellAllocationOverTimePlot.h"
|
||||
#include "RimWellAllocationPlot.h"
|
||||
#include "RimWellPath.h"
|
||||
|
||||
@@ -108,7 +109,11 @@ void RicShowWellAllocationPlotFeature::onActionTriggered( bool isChecked )
|
||||
flowPlotColl->defaultWellAllocPlot()->setFromSimulationWell( simWell );
|
||||
flowPlotColl->defaultWellAllocPlot()->updateConnectedEditors();
|
||||
|
||||
flowPlotColl->defaultWellAllocOverTimePlot()->setFromSimulationWell( simWell );
|
||||
flowPlotColl->defaultWellAllocOverTimePlot()->updateConnectedEditors();
|
||||
|
||||
RiuPlotMainWindowTools::showPlotMainWindow();
|
||||
RiuPlotMainWindowTools::onObjectAppended( flowPlotColl->defaultWellAllocOverTimePlot() );
|
||||
RiuPlotMainWindowTools::onObjectAppended( flowPlotColl->defaultWellAllocPlot() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ set(SOURCE_GROUP_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWellRftEnsembleCurveSet.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWellDistributionPlot.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWellDistributionPlotCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWellAllocationOverTimePlot.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWellAllocationTools.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWellAllocationOverTimeCollection.h
|
||||
)
|
||||
|
||||
set(SOURCE_GROUP_SOURCE_FILES
|
||||
@@ -32,6 +35,9 @@ set(SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWellRftEnsembleCurveSet.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWellDistributionPlot.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWellDistributionPlotCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWellAllocationOverTimePlot.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWellAllocationTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWellAllocationOverTimeCollection.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "RimFlowCharacteristicsPlot.h"
|
||||
#include "RimProject.h"
|
||||
#include "RimWellAllocationOverTimePlot.h"
|
||||
#include "RimWellAllocationPlot.h"
|
||||
#include "RimWellDistributionPlotCollection.h"
|
||||
|
||||
@@ -38,12 +39,12 @@ RimFlowPlotCollection::RimFlowPlotCollection()
|
||||
CAF_PDM_InitFieldNoDefault( &m_flowCharacteristicsPlot, "FlowCharacteristicsPlot", "" );
|
||||
m_flowCharacteristicsPlot.uiCapability()->setUiTreeHidden( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_defaultWellAllocOverTimePlot, "DefaultWellAllocationOverTimePlot", "" );
|
||||
m_defaultWellAllocOverTimePlot.uiCapability()->setUiTreeHidden( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_defaultWellAllocPlot, "DefaultWellAllocationPlot", "" );
|
||||
m_defaultWellAllocPlot.uiCapability()->setUiTreeHidden( true );
|
||||
|
||||
// CAF_PDM_InitFieldNoDefault( &m_dbgWellDistributionPlot, "DbgWellDistributionPlot", "");
|
||||
// m_dbgWellDistributionPlot.uiCapability()->setUiHidden( true );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_wellDistributionPlotCollection, "WellDistributionPlotCollection", "" );
|
||||
m_wellDistributionPlotCollection.uiCapability()->setUiTreeHidden( true );
|
||||
|
||||
@@ -73,9 +74,8 @@ void RimFlowPlotCollection::deleteAllPlots()
|
||||
m_defaultWellAllocPlot->removeFromMdiAreaAndDeleteViewWidget();
|
||||
delete m_defaultWellAllocPlot();
|
||||
}
|
||||
|
||||
delete m_defaultWellAllocOverTimePlot;
|
||||
delete m_flowCharacteristicsPlot;
|
||||
// delete m_dbgWellDistributionPlot;
|
||||
delete m_wellDistributionPlotCollection;
|
||||
|
||||
m_storedWellAllocPlots.deleteChildren();
|
||||
@@ -87,11 +87,14 @@ void RimFlowPlotCollection::deleteAllPlots()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimFlowPlotCollection::loadDataAndUpdateAllPlots()
|
||||
{
|
||||
caf::ProgressInfo plotProgress( m_storedWellAllocPlots.size() + m_storedFlowCharacteristicsPlots.size() + 3, "" );
|
||||
caf::ProgressInfo plotProgress( m_storedWellAllocPlots.size() + m_storedFlowCharacteristicsPlots.size() + 4, "" );
|
||||
|
||||
if ( m_defaultWellAllocPlot ) m_defaultWellAllocPlot->loadDataAndUpdate();
|
||||
plotProgress.incrementProgress();
|
||||
|
||||
if ( m_defaultWellAllocOverTimePlot ) m_defaultWellAllocOverTimePlot->loadDataAndUpdate();
|
||||
plotProgress.incrementProgress();
|
||||
|
||||
for ( RimWellAllocationPlot* p : m_storedWellAllocPlots )
|
||||
{
|
||||
p->loadDataAndUpdate();
|
||||
@@ -109,11 +112,6 @@ void RimFlowPlotCollection::loadDataAndUpdateAllPlots()
|
||||
m_flowCharacteristicsPlot->loadDataAndUpdate();
|
||||
}
|
||||
|
||||
// if ( m_dbgWellDistributionPlot )
|
||||
//{
|
||||
// m_dbgWellDistributionPlot->loadDataAndUpdate();
|
||||
//}
|
||||
|
||||
if ( m_wellDistributionPlotCollection )
|
||||
{
|
||||
m_wellDistributionPlotCollection->loadDataAndUpdate();
|
||||
@@ -126,7 +124,8 @@ void RimFlowPlotCollection::loadDataAndUpdateAllPlots()
|
||||
size_t RimFlowPlotCollection::plotCount() const
|
||||
{
|
||||
size_t plotCount = 0;
|
||||
if ( m_defaultWellAllocPlot ) plotCount = 1;
|
||||
plotCount += m_defaultWellAllocPlot ? 1 : 0;
|
||||
plotCount += m_defaultWellAllocOverTimePlot ? 1 : 0;
|
||||
plotCount += m_storedWellAllocPlots.size();
|
||||
plotCount += m_storedFlowCharacteristicsPlots.size();
|
||||
return plotCount;
|
||||
@@ -148,6 +147,22 @@ void RimFlowPlotCollection::addFlowCharacteristicsPlotToStoredPlots( RimFlowChar
|
||||
m_storedFlowCharacteristicsPlots.push_back( plot );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimWellAllocationOverTimePlot* RimFlowPlotCollection::defaultWellAllocOverTimePlot()
|
||||
{
|
||||
if ( !m_defaultWellAllocOverTimePlot() )
|
||||
{
|
||||
m_defaultWellAllocOverTimePlot = new RimWellAllocationOverTimePlot;
|
||||
m_defaultWellAllocOverTimePlot->setDescription( "Default Well Allocation Over Time Plot" );
|
||||
}
|
||||
|
||||
this->updateConnectedEditors();
|
||||
|
||||
return m_defaultWellAllocOverTimePlot();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -198,6 +213,12 @@ void RimFlowPlotCollection::ensureDefaultFlowPlotsAreCreated()
|
||||
m_defaultWellAllocPlot->setDescription( "Default Flow Diagnostics Plot" );
|
||||
}
|
||||
|
||||
if ( !m_defaultWellAllocOverTimePlot() )
|
||||
{
|
||||
m_defaultWellAllocOverTimePlot = new RimWellAllocationOverTimePlot;
|
||||
m_defaultWellAllocOverTimePlot->setDescription( "Default Well Allocation Over Time Plot" );
|
||||
}
|
||||
|
||||
if ( !m_flowCharacteristicsPlot() )
|
||||
{
|
||||
m_flowCharacteristicsPlot = new RimFlowCharacteristicsPlot;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "cafPdmChildField.h"
|
||||
#include "cafPdmObject.h"
|
||||
|
||||
class RimWellAllocationOverTimePlot;
|
||||
class RimWellAllocationPlot;
|
||||
class RimFlowCharacteristicsPlot;
|
||||
class RimWellDistributionPlot;
|
||||
@@ -47,6 +48,7 @@ public:
|
||||
|
||||
void addWellAllocPlotToStoredPlots( RimWellAllocationPlot* plot );
|
||||
void addFlowCharacteristicsPlotToStoredPlots( RimFlowCharacteristicsPlot* plot );
|
||||
RimWellAllocationOverTimePlot* defaultWellAllocOverTimePlot();
|
||||
RimWellAllocationPlot* defaultWellAllocPlot();
|
||||
RimFlowCharacteristicsPlot* defaultFlowCharacteristicsPlot();
|
||||
RimWellDistributionPlotCollection* wellDistributionPlotCollection() const;
|
||||
@@ -54,6 +56,7 @@ public:
|
||||
|
||||
private:
|
||||
caf::PdmChildField<RimFlowCharacteristicsPlot*> m_flowCharacteristicsPlot;
|
||||
caf::PdmChildField<RimWellAllocationOverTimePlot*> m_defaultWellAllocOverTimePlot;
|
||||
caf::PdmChildField<RimWellAllocationPlot*> m_defaultWellAllocPlot;
|
||||
caf::PdmChildField<RimWellDistributionPlotCollection*> m_wellDistributionPlotCollection;
|
||||
caf::PdmChildArrayField<RimWellAllocationPlot*> m_storedWellAllocPlots;
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2023- Equinor ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RimWellAllocationOverTimeCollection.h"
|
||||
|
||||
#include "cafAssert.h"
|
||||
|
||||
#include "RigAccWellFlowCalculator.h"
|
||||
#include "RigFlowDiagResultAddress.h"
|
||||
#include "RigWellResultPoint.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimWellAllocationOverTimeCollection::RimWellAllocationOverTimeCollection(
|
||||
const std::vector<QDateTime>& timeStepDates,
|
||||
const std::map<QDateTime, RigAccWellFlowCalculator>& timeStepAndCalculatorPairs )
|
||||
: m_timeStepDates( timeStepDates )
|
||||
, m_timeStepAndCalculatorPairs( timeStepAndCalculatorPairs )
|
||||
{
|
||||
for ( const auto& [date, calculator] : m_timeStepAndCalculatorPairs )
|
||||
{
|
||||
std::string err = "Calculator for time step date " + date.toString().toStdString() +
|
||||
" does not exist in time step dates vector ";
|
||||
CAF_ASSERT( std::find( m_timeStepDates.begin(), m_timeStepDates.end(), date ) != m_timeStepDates.end() &&
|
||||
err.data() );
|
||||
}
|
||||
|
||||
std::sort( m_timeStepDates.begin(), m_timeStepDates.end() );
|
||||
|
||||
// Retrieve union of well names across all calculators
|
||||
std::set<QString> allWellNames;
|
||||
for ( const auto& [date, calculator] : m_timeStepAndCalculatorPairs )
|
||||
{
|
||||
allWellNames.insert( calculator.tracerNames().begin(), calculator.tracerNames().end() );
|
||||
}
|
||||
|
||||
// Fill default well values into map
|
||||
const double defaultValue = 0.0;
|
||||
for ( const auto& well : allWellNames )
|
||||
{
|
||||
for ( const auto& date : m_timeStepDates )
|
||||
{
|
||||
std::pair<QDateTime, double> defaultPair( date, defaultValue );
|
||||
m_defaultWellValuesMap[well].insert( defaultPair );
|
||||
}
|
||||
}
|
||||
m_wellValuesMap = m_defaultWellValuesMap;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimeCollection::fillWithFlowRatePercentageValues()
|
||||
{
|
||||
m_wellValuesMap = m_defaultWellValuesMap;
|
||||
for ( auto& [timeStep, calculator] : m_timeStepAndCalculatorPairs )
|
||||
{
|
||||
const auto totalTracerFractions = calculator.totalTracerFractions();
|
||||
for ( const auto& [wellName, value] : totalTracerFractions )
|
||||
{
|
||||
double valuePercent = 100.0 * value;
|
||||
m_wellValuesMap[wellName][timeStep] = valuePercent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimeCollection::fillWithFlowRateValues()
|
||||
{
|
||||
m_wellValuesMap = m_defaultWellValuesMap;
|
||||
const size_t branchIdx = 0;
|
||||
for ( auto& [timeStep, calculator] : m_timeStepAndCalculatorPairs )
|
||||
{
|
||||
for ( const auto& wellName : calculator.tracerNames() )
|
||||
{
|
||||
const auto& accumulatedConnectionFlows = calculator.accumulatedTracerFlowPrConnection( wellName, branchIdx );
|
||||
const double topConnectionFlow = accumulatedConnectionFlows.empty() ? 0.0 : accumulatedConnectionFlows.back();
|
||||
m_wellValuesMap[wellName][timeStep] = topConnectionFlow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Fill with flow volume at time step.
|
||||
///
|
||||
/// Create volume by multiplying with number of days since last time step.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimeCollection::fillWithFlowVolumeValues()
|
||||
{
|
||||
fillWithFlowRateValues();
|
||||
|
||||
for ( auto& [well, timeStepsAndValues] : m_wellValuesMap )
|
||||
{
|
||||
QDateTime prevTimeStep;
|
||||
for ( auto& [timeStep, value] : timeStepsAndValues )
|
||||
{
|
||||
if ( !prevTimeStep.isValid() )
|
||||
{
|
||||
prevTimeStep = timeStep;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto numDays = static_cast<double>( prevTimeStep.daysTo( timeStep ) );
|
||||
value = value * numDays;
|
||||
prevTimeStep = timeStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Fill with accumulated flow volume over a range of time steps. Create volume by multiplying with
|
||||
/// number of days since last time step.
|
||||
///
|
||||
/// Group small contributors in "Others" if accumulated volume value at last time step is below
|
||||
/// threshold value.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimeCollection::fillWithAccumulatedFlowVolumeValues( double smallContributionsThreshold )
|
||||
{
|
||||
fillWithFlowRateValues();
|
||||
|
||||
for ( auto& [well, timeStepsAndValues] : m_wellValuesMap )
|
||||
{
|
||||
QDateTime prevTimeStep;
|
||||
double accumulatedVolume = 0.0;
|
||||
for ( auto& [timeStep, value] : timeStepsAndValues )
|
||||
{
|
||||
if ( !prevTimeStep.isValid() )
|
||||
{
|
||||
prevTimeStep = timeStep;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto numDays = static_cast<double>( prevTimeStep.daysTo( timeStep ) );
|
||||
const double volume = value * numDays;
|
||||
accumulatedVolume += volume;
|
||||
value = accumulatedVolume;
|
||||
prevTimeStep = timeStep;
|
||||
}
|
||||
}
|
||||
|
||||
if ( smallContributionsThreshold > 0.0 )
|
||||
{
|
||||
groupAccumulatedFlowVolumes( m_wellValuesMap, smallContributionsThreshold );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Fill with accumulated well flow volumes in percent of total accumulated flow volume at each
|
||||
/// time step.
|
||||
///
|
||||
///
|
||||
/// Group small contributors in "Others" if percentage value for well is below threshold at every
|
||||
/// time step.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimeCollection::fillWithAccumulatedFlowVolumePercentageValues( double smallContributionsThreshold )
|
||||
{
|
||||
// Handle threshold filtering afterwards
|
||||
const double nonFilteringThreshold = 0.0;
|
||||
fillWithAccumulatedFlowVolumeValues( nonFilteringThreshold );
|
||||
|
||||
for ( const auto& timeStep : m_timeStepDates )
|
||||
{
|
||||
double totalAccumulatedVolume = 0.0;
|
||||
std::map<QString, double> timeStepWellValues;
|
||||
|
||||
// Sum accumulated volumes at time step
|
||||
for ( auto& [well, values] : m_wellValuesMap )
|
||||
{
|
||||
const auto accumulatedVolume = values[timeStep];
|
||||
totalAccumulatedVolume += accumulatedVolume;
|
||||
timeStepWellValues[well] = accumulatedVolume;
|
||||
}
|
||||
|
||||
// If no accumulated volume exist at time step
|
||||
if ( totalAccumulatedVolume == 0.0 ) continue;
|
||||
|
||||
// Create percentage value
|
||||
for ( auto& [well, value] : timeStepWellValues )
|
||||
{
|
||||
m_wellValuesMap[well][timeStep] = 100.0 * value / totalAccumulatedVolume;
|
||||
}
|
||||
}
|
||||
|
||||
if ( smallContributionsThreshold > 0.0 )
|
||||
{
|
||||
const auto percentageThreshold = 100.0 * smallContributionsThreshold;
|
||||
groupAccumulatedFlowVolumePercentages( m_wellValuesMap, percentageThreshold );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Handle grouping of small contributors in accumulated volume data based on threshold.
|
||||
/// Group small contributors in "Others" if accumulated volume value at last time step is below
|
||||
/// threshold value.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimeCollection::groupAccumulatedFlowVolumes( std::map<QString, std::map<QDateTime, double>>& rWellValuesMap,
|
||||
double threshold )
|
||||
{
|
||||
if ( m_timeStepDates.empty() ) return;
|
||||
|
||||
std::map<QString, std::map<QDateTime, double>> groupedWellValuesMap;
|
||||
std::map<QString, double> lastAccumulatedWellValues;
|
||||
double sumLastAccumulatedWellValues = 0.0;
|
||||
const QDateTime lastTimeStep = m_timeStepDates.back();
|
||||
for ( auto& [well, values] : rWellValuesMap )
|
||||
{
|
||||
const double lastWellValue = values[lastTimeStep];
|
||||
lastAccumulatedWellValues[well] = lastWellValue;
|
||||
sumLastAccumulatedWellValues += lastWellValue;
|
||||
}
|
||||
|
||||
// Filter out wells with accumulated flow less than threshold and place among "others"
|
||||
std::vector<QString> contributingWells;
|
||||
std::vector<QString> groupedWells;
|
||||
for ( const auto& [well, value] : lastAccumulatedWellValues )
|
||||
{
|
||||
if ( sumLastAccumulatedWellValues > 0.0 && ( value / sumLastAccumulatedWellValues ) < threshold )
|
||||
{
|
||||
groupedWells.push_back( well );
|
||||
}
|
||||
else
|
||||
{
|
||||
contributingWells.push_back( well );
|
||||
}
|
||||
}
|
||||
|
||||
for ( const auto& well : contributingWells )
|
||||
{
|
||||
groupedWellValuesMap[well] = rWellValuesMap[well];
|
||||
}
|
||||
for ( const auto& well : groupedWells )
|
||||
{
|
||||
if ( groupedWellValuesMap.count( RIG_TINY_TRACER_GROUP_NAME ) == 0 )
|
||||
{
|
||||
groupedWellValuesMap[RIG_TINY_TRACER_GROUP_NAME] = rWellValuesMap[well];
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( const auto& [date, value] : rWellValuesMap[well] )
|
||||
{
|
||||
groupedWellValuesMap[RIG_TINY_TRACER_GROUP_NAME][date] += value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rWellValuesMap = groupedWellValuesMap;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Handle grouping of small contributors in accumulated volume percentage based on threshold.
|
||||
/// Group small contributors in "Others" if percentage value for well is below threshold at every
|
||||
/// time step. If percentage value is above threshold for one time step or more, show data for well
|
||||
/// at every time step.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimeCollection::groupAccumulatedFlowVolumePercentages(
|
||||
std::map<QString, std::map<QDateTime, double>>& rWellValuesMap,
|
||||
double thresholdPercent )
|
||||
{
|
||||
auto getMaxValue = []( const std::map<QDateTime, double>& valuesMap ) -> double {
|
||||
double maxValue = 0.0;
|
||||
for ( const auto& [timeStep, value] : valuesMap )
|
||||
{
|
||||
maxValue = value > maxValue ? value : maxValue;
|
||||
}
|
||||
return maxValue;
|
||||
};
|
||||
|
||||
std::vector<QString> contributingWells;
|
||||
std::vector<QString> groupedWells;
|
||||
for ( const auto& [well, values] : rWellValuesMap )
|
||||
{
|
||||
const double maxValue = getMaxValue( values );
|
||||
if ( maxValue > thresholdPercent )
|
||||
{
|
||||
contributingWells.push_back( well );
|
||||
}
|
||||
else
|
||||
{
|
||||
groupedWells.push_back( well );
|
||||
}
|
||||
}
|
||||
|
||||
std::map<QString, std::map<QDateTime, double>> groupedWellValuesMap;
|
||||
for ( const auto& well : contributingWells )
|
||||
{
|
||||
groupedWellValuesMap[well] = rWellValuesMap[well];
|
||||
}
|
||||
for ( const auto& well : groupedWells )
|
||||
{
|
||||
if ( groupedWellValuesMap.count( RIG_TINY_TRACER_GROUP_NAME ) == 0 )
|
||||
{
|
||||
groupedWellValuesMap[RIG_TINY_TRACER_GROUP_NAME] = rWellValuesMap[well];
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( const auto& [date, value] : rWellValuesMap[well] )
|
||||
{
|
||||
groupedWellValuesMap[RIG_TINY_TRACER_GROUP_NAME][date] += value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rWellValuesMap = groupedWellValuesMap;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2023- Equinor ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QString>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class RigAccWellFlowCalculator;
|
||||
|
||||
class RimWellAllocationOverTimeCollection
|
||||
{
|
||||
public:
|
||||
RimWellAllocationOverTimeCollection( const std::vector<QDateTime>& timeStepDates,
|
||||
const std::map<QDateTime, RigAccWellFlowCalculator>& timeStepAndCalculatorPairs );
|
||||
|
||||
const std::vector<QDateTime> timeStepDates() const { return m_timeStepDates; }
|
||||
const std::map<QString, std::map<QDateTime, double>> wellValuesMap() const { return m_wellValuesMap; }
|
||||
|
||||
void fillWithFlowRatePercentageValues();
|
||||
void fillWithFlowRateValues();
|
||||
void fillWithFlowVolumeValues();
|
||||
void fillWithAccumulatedFlowVolumeValues( double smallContributionsThreshold );
|
||||
void fillWithAccumulatedFlowVolumePercentageValues( double smallContributionsThreshold );
|
||||
|
||||
private:
|
||||
void groupAccumulatedFlowVolumes( std::map<QString, std::map<QDateTime, double>>& rWellValuesMap, double threshold );
|
||||
void groupAccumulatedFlowVolumePercentages( std::map<QString, std::map<QDateTime, double>>& rWellValuesMap,
|
||||
double threshold );
|
||||
|
||||
private:
|
||||
const std::map<QDateTime, RigAccWellFlowCalculator>& m_timeStepAndCalculatorPairs;
|
||||
std::vector<QDateTime> m_timeStepDates;
|
||||
std::map<QString, std::map<QDateTime, double>> m_defaultWellValuesMap;
|
||||
std::map<QString, std::map<QDateTime, double>> m_wellValuesMap;
|
||||
};
|
||||
@@ -0,0 +1,728 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimWellAllocationOverTimePlot.h"
|
||||
|
||||
#include "RiaColorTools.h"
|
||||
#include "RiaDefines.h"
|
||||
#include "RiaLogging.h"
|
||||
#include "RiaPreferences.h"
|
||||
#include "RiaQDateTimeTools.h"
|
||||
|
||||
#include "RigAccWellFlowCalculator.h"
|
||||
#include "RigEclipseCaseData.h"
|
||||
#include "RigSimWellData.h"
|
||||
#include "RigSimulationWellCenterLineCalculator.h"
|
||||
#include "RigWellResultPoint.h"
|
||||
|
||||
#include "RimEclipseCaseTools.h"
|
||||
#include "RimEclipseCellColors.h"
|
||||
#include "RimEclipseResultCase.h"
|
||||
#include "RimEclipseView.h"
|
||||
#include "RimFlowDiagSolution.h"
|
||||
#include "RimSimWellInView.h"
|
||||
#include "RimStackablePlotCurve.h"
|
||||
#include "RimWellAllocationOverTimeCollection.h"
|
||||
#include "RimWellAllocationTools.h"
|
||||
#include "RimWellLogFile.h"
|
||||
#include "RimWellPlotTools.h"
|
||||
|
||||
#include "RiuContextMenuLauncher.h"
|
||||
#include "RiuPlotCurve.h"
|
||||
#include "RiuPlotWidget.h"
|
||||
#include "RiuQwtCurvePointTracker.h"
|
||||
#include "RiuQwtPlotTools.h"
|
||||
#include "RiuQwtPlotWidget.h"
|
||||
|
||||
#include "cafCmdFeatureMenuBuilder.h"
|
||||
#include "cafPdmUiComboBoxEditor.h"
|
||||
#include "cafPdmUiPushButtonEditor.h"
|
||||
#include "cafPdmUiTreeSelectionEditor.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimWellAllocationOverTimePlot, "RimWellAllocationOverTimePlot" );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
namespace caf
|
||||
{
|
||||
template <>
|
||||
void AppEnum<RimWellAllocationOverTimePlot::FlowValueType>::setUp()
|
||||
{
|
||||
addItem( RimWellAllocationOverTimePlot::FlowValueType::FLOW_RATE, "FLOW_RATE", "Flow Rates" );
|
||||
addItem( RimWellAllocationOverTimePlot::FlowValueType::FLOW_RATE_PERCENTAGE,
|
||||
"FLOW_RATE_PERCENTAGE",
|
||||
"Flow Rate Percentage" );
|
||||
addItem( RimWellAllocationOverTimePlot::FlowValueType::FLOW_VOLUME, "FLOW_VOLUME", "Flow Volumes" );
|
||||
addItem( RimWellAllocationOverTimePlot::FlowValueType::ACCUMULATED_FLOW_VOLUME,
|
||||
"ACCUMULATED_FLOW_VOLUME",
|
||||
"Accumulated Flow Volumes" );
|
||||
addItem( RimWellAllocationOverTimePlot::FlowValueType::ACCUMULATED_FLOW_VOLUME_PERCENTAGE,
|
||||
"ACCUMULATED_FLOW_VOLUME_PERCENTAGE",
|
||||
"Accumulated Flow Volume Percentage" );
|
||||
setDefault( RimWellAllocationOverTimePlot::FlowValueType::FLOW_RATE );
|
||||
}
|
||||
} // namespace caf
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimWellAllocationOverTimePlot::RimWellAllocationOverTimePlot()
|
||||
{
|
||||
// TODO: Add icon
|
||||
CAF_PDM_InitObject( "Well Allocation Over Time Plot", ":/WellAllocOverTimePlot16x16.png" );
|
||||
|
||||
CAF_PDM_InitField( &m_userName, "PlotDescription", QString( "Well Allocation Over Time Plot" ), "Name" );
|
||||
m_userName.uiCapability()->setUiReadOnly( true );
|
||||
CAF_PDM_InitFieldNoDefault( &m_case, "CurveCase", "Case" );
|
||||
m_case.uiCapability()->setUiTreeChildrenHidden( true );
|
||||
CAF_PDM_InitField( &m_wellName, "WellName", QString( "None" ), "Well" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_selectedFromTimeStep, "FromTimeStep", "From Time Step" );
|
||||
m_selectedFromTimeStep.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() );
|
||||
CAF_PDM_InitFieldNoDefault( &m_selectedToTimeStep, "ToTimeStep", "To Time Step" );
|
||||
m_selectedToTimeStep.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() );
|
||||
CAF_PDM_InitFieldNoDefault( &m_excludeTimeSteps, "ExcludeTimeSteps", "" );
|
||||
m_excludeTimeSteps.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
|
||||
CAF_PDM_InitFieldNoDefault( &m_applyExcludeTimeSteps, "ApplyExcludeTimeSteps", "" );
|
||||
caf::PdmUiPushButtonEditor::configureEditorForField( &m_applyExcludeTimeSteps );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_flowDiagSolution, "FlowDiagSolution", "Plot Type" );
|
||||
CAF_PDM_InitFieldNoDefault( &m_flowValueType, "FlowValueType", "Value Type" );
|
||||
CAF_PDM_InitField( &m_groupSmallContributions, "GroupSmallContributions", true, "Group Small Contributions" );
|
||||
CAF_PDM_InitField( &m_smallContributionsThreshold, "SmallContributionsThreshold", 0.005, "Threshold" );
|
||||
|
||||
setAsPlotMdiWindow();
|
||||
setShowWindow( false );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimWellAllocationOverTimePlot::~RimWellAllocationOverTimePlot()
|
||||
{
|
||||
removeMdiWindowFromMdiArea();
|
||||
deleteViewWidget();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimePlot::setDescription( const QString& description )
|
||||
{
|
||||
m_userName = description;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimePlot::setFromSimulationWell( RimSimWellInView* simWell )
|
||||
{
|
||||
RimEclipseView* eclView;
|
||||
simWell->firstAncestorOrThisOfType( eclView );
|
||||
RimEclipseResultCase* eclCase;
|
||||
simWell->firstAncestorOrThisOfType( eclCase );
|
||||
|
||||
m_case = eclCase;
|
||||
m_wellName = simWell->simWellData()->m_wellName;
|
||||
|
||||
setValidTimeStepRangeForCase();
|
||||
|
||||
// Use the active flow diagnostics solutions, or the first one as default
|
||||
m_flowDiagSolution = eclView->cellResult()->flowDiagSolution();
|
||||
if ( !m_flowDiagSolution )
|
||||
{
|
||||
m_flowDiagSolution = m_case->defaultFlowDiagSolution();
|
||||
}
|
||||
|
||||
onLoadDataAndUpdate();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RiuPlotWidget* RimWellAllocationOverTimePlot::plotWidget()
|
||||
{
|
||||
return m_plotWidget;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RimWellAllocationOverTimePlot::asciiDataForPlotExport() const
|
||||
{
|
||||
// Retrieve collection of allocation over time data for wells
|
||||
RimWellAllocationOverTimeCollection allocationOverTimeCollection = createWellAllocationOverTimeCollection();
|
||||
|
||||
QString titleText = m_userName + "\n\n";
|
||||
|
||||
QString dataText = "Time Step\t";
|
||||
for ( auto& [wellName, wellValues] : allocationOverTimeCollection.wellValuesMap() )
|
||||
{
|
||||
dataText += wellName + "\t";
|
||||
}
|
||||
dataText += "\n";
|
||||
|
||||
const QString dateFormatStr = dateFormatString();
|
||||
for ( const auto& timeStep : allocationOverTimeCollection.timeStepDates() )
|
||||
{
|
||||
dataText += timeStep.toString( dateFormatStr ) + "\t";
|
||||
for ( auto& [wellName, wellValues] : allocationOverTimeCollection.wellValuesMap() )
|
||||
{
|
||||
dataText += wellValues.count( timeStep ) == 0 ? QString::number( 0.0 )
|
||||
: QString::number( wellValues.at( timeStep ) );
|
||||
dataText += "\t";
|
||||
}
|
||||
dataText += "\n";
|
||||
}
|
||||
|
||||
return titleText + dataText;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RimWellAllocationOverTimePlot::description() const
|
||||
{
|
||||
return uiName();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QWidget* RimWellAllocationOverTimePlot::viewWidget()
|
||||
{
|
||||
return plotWidget();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QImage RimWellAllocationOverTimePlot::snapshotWindowContent()
|
||||
{
|
||||
QImage image;
|
||||
|
||||
if ( m_plotWidget )
|
||||
{
|
||||
QPixmap pix = m_plotWidget->grab();
|
||||
image = pix.toImage();
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RiuPlotWidget* RimWellAllocationOverTimePlot::doCreatePlotViewWidget( QWidget* mainWindowParent )
|
||||
{
|
||||
// If called multiple times?
|
||||
if ( m_plotWidget )
|
||||
{
|
||||
return m_plotWidget;
|
||||
}
|
||||
auto* plotWidget = new RiuQwtPlotWidget( this, mainWindowParent );
|
||||
new RiuQwtCurvePointTracker( plotWidget->qwtPlot(), true, nullptr );
|
||||
|
||||
// Remove event filter to disable unwanted highlighting on left click in plot.
|
||||
plotWidget->removeEventFilter();
|
||||
|
||||
caf::CmdFeatureMenuBuilder menuBuilder;
|
||||
menuBuilder << "RicShowPlotDataFeature";
|
||||
new RiuContextMenuLauncher( plotWidget, menuBuilder );
|
||||
|
||||
m_plotWidget = plotWidget;
|
||||
m_plotWidget->setAxisTitleEnabled( RiuPlotAxis::defaultLeft(), true );
|
||||
|
||||
RiuQwtPlotTools::enableDateBasedBottomXAxis( m_plotWidget->qwtPlot(),
|
||||
RiaPreferences::current()->dateFormat(),
|
||||
QString(),
|
||||
RiaDefines::DateFormatComponents::DATE_FORMAT_YEAR_MONTH_DAY,
|
||||
RiaDefines::TimeFormatComponents::TIME_FORMAT_NONE );
|
||||
|
||||
updateLegend();
|
||||
onLoadDataAndUpdate();
|
||||
|
||||
return m_plotWidget;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimePlot::deleteViewWidget()
|
||||
{
|
||||
if ( m_plotWidget != nullptr )
|
||||
{
|
||||
m_plotWidget->hide();
|
||||
m_plotWidget->setParent( nullptr );
|
||||
delete m_plotWidget;
|
||||
m_plotWidget = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimePlot::onLoadDataAndUpdate()
|
||||
{
|
||||
updateMdiWindowVisibility();
|
||||
|
||||
if ( m_plotWidget == nullptr || m_case == nullptr )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If no 3D view is open, we have to make sure the case is opened
|
||||
if ( !m_case->ensureReservoirCaseIsOpen() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
updateFromWell();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimePlot::updateFromWell()
|
||||
{
|
||||
if ( !m_plotWidget )
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_plotWidget->insertLegend( RiuPlotWidget::Legend::BOTTOM );
|
||||
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 );
|
||||
|
||||
// Negative z-position to show grid lines
|
||||
int zPos = -10000;
|
||||
for ( auto& [wellName, wellValues] : allocationOverTimeCollection.wellValuesMap() )
|
||||
{
|
||||
cvf::Color3f color = m_flowDiagSolution ? m_flowDiagSolution->tracerColor( wellName ) : getTracerColor( wellName );
|
||||
for ( size_t i = 0; i < allocationOverTimeCollection.timeStepDates().size(); ++i )
|
||||
{
|
||||
const auto value = wellValues.at( allocationOverTimeCollection.timeStepDates()[i] );
|
||||
allStackedValues[i] += value;
|
||||
}
|
||||
|
||||
const auto qColor = QColor( color.rByte(), color.gByte(), color.bByte() );
|
||||
const auto fillColor = RiaColorTools::blendQColors( qColor, QColor( Qt::white ), 3, 1 );
|
||||
const QBrush fillBrush( fillColor, Qt::BrushStyle::SolidPattern );
|
||||
auto interpolationType = m_flowValueType == FlowValueType::ACCUMULATED_FLOW_VOLUME
|
||||
? RiuQwtPlotCurveDefines::CurveInterpolationEnum::INTERPOLATION_POINT_TO_POINT
|
||||
: RiuQwtPlotCurveDefines::CurveInterpolationEnum::INTERPOLATION_STEP_LEFT;
|
||||
|
||||
RiuPlotCurve* curve = m_plotWidget->createPlotCurve( nullptr, wellName );
|
||||
curve->setAppearance( RiuQwtPlotCurveDefines::LineStyleEnum::STYLE_SOLID, interpolationType, 2, qColor, fillBrush );
|
||||
curve->setSamplesFromDatesAndYValues( allocationOverTimeCollection.timeStepDates(), allStackedValues, false );
|
||||
curve->attachToPlot( m_plotWidget );
|
||||
curve->showInPlot();
|
||||
curve->setZ( zPos-- );
|
||||
}
|
||||
|
||||
QString descriptionText = QString( m_flowDiagSolution() ? "Well Allocation Over Time: " : "Well Flow Over Time: " ) +
|
||||
QString( "%1 (%2)" ).arg( m_wellName ).arg( m_case->caseUserDescription() );
|
||||
QString valueTypeText = getValueTypeText();
|
||||
QString newDescription = descriptionText + ", " + valueTypeText;
|
||||
|
||||
setDescription( newDescription );
|
||||
m_plotWidget->setWindowTitle( newDescription );
|
||||
m_plotWidget->setPlotTitle( descriptionText + "<br>" + valueTypeText + "</br>" );
|
||||
|
||||
m_plotWidget->setAxisTitleText( RiuPlotAxis::defaultLeft(), valueTypeText );
|
||||
m_plotWidget->scheduleReplot();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Create well flow calculator per time step date, retrieve total tracer fractions and propagate
|
||||
/// well data for all time steps. If well does not exist for specific time step date - value is
|
||||
/// set to 0.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimWellAllocationOverTimeCollection RimWellAllocationOverTimePlot::createWellAllocationOverTimeCollection() const
|
||||
{
|
||||
if ( !m_case )
|
||||
{
|
||||
return RimWellAllocationOverTimeCollection( {}, {} );
|
||||
}
|
||||
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( {}, {} );
|
||||
}
|
||||
const RigSimWellData* simWellData = m_case->eclipseCaseData()->findSimWellData( m_wellName );
|
||||
if ( !simWellData )
|
||||
{
|
||||
return RimWellAllocationOverTimeCollection( {}, {} );
|
||||
}
|
||||
|
||||
// Note: Threshold per calculator does not work for accumulated data - use no threshold for each calculator
|
||||
// and filter on threshold value after accumulating non-filtered values.
|
||||
const double smallContributionThreshold = m_groupSmallContributions() &&
|
||||
m_flowValueType != FlowValueType::ACCUMULATED_FLOW_VOLUME &&
|
||||
m_flowValueType != FlowValueType::ACCUMULATED_FLOW_VOLUME_PERCENTAGE
|
||||
? m_smallContributionsThreshold
|
||||
: 0.0;
|
||||
|
||||
auto isTimeStepInSelectedRange = [&]( const QDateTime& timeStep ) -> bool {
|
||||
return m_selectedFromTimeStep() <= timeStep && timeStep <= m_selectedToTimeStep();
|
||||
};
|
||||
|
||||
std::map<QDateTime, RigAccWellFlowCalculator> timeStepAndCalculatorPairs;
|
||||
std::set<QDateTime> excludedTimeSteps = std::set( m_excludeTimeSteps().begin(), m_excludeTimeSteps().end() );
|
||||
std::vector<QDateTime> allTimeSteps = m_case->timeStepDates();
|
||||
std::vector<QDateTime> selectedTimeSteps;
|
||||
std::copy_if( allTimeSteps.begin(), allTimeSteps.end(), std::back_inserter( selectedTimeSteps ), isTimeStepInSelectedRange );
|
||||
|
||||
const bool branchDetection = false;
|
||||
for ( size_t i = 0; i < allTimeSteps.size(); ++i )
|
||||
{
|
||||
// NOTE: Must have all time step dates for case due to have correct time step index for simulation well data
|
||||
if ( !isTimeStepInSelectedRange( allTimeSteps[i] ) ||
|
||||
excludedTimeSteps.find( allTimeSteps[i] ) != excludedTimeSteps.end() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::vector<cvf::Vec3d>> pipeBranchesCLCoords;
|
||||
std::vector<std::vector<RigWellResultPoint>> pipeBranchesCellIds;
|
||||
std::map<QString, const std::vector<double>*> tracerFractionCellValues =
|
||||
RimWellAllocationTools::findOrCreateRelevantTracerCellFractions( simWellData, m_flowDiagSolution, i );
|
||||
|
||||
RigSimulationWellCenterLineCalculator::calculateWellPipeCenterlineFromWellFrame( m_case->eclipseCaseData(),
|
||||
simWellData,
|
||||
i,
|
||||
branchDetection,
|
||||
true,
|
||||
pipeBranchesCLCoords,
|
||||
pipeBranchesCellIds );
|
||||
|
||||
if ( tracerFractionCellValues.size() )
|
||||
{
|
||||
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 ) );
|
||||
const auto calculator = RigAccWellFlowCalculator( pipeBranchesCLCoords,
|
||||
pipeBranchesCellIds,
|
||||
tracerFractionCellValues,
|
||||
cellIdxCalc,
|
||||
smallContributionThreshold,
|
||||
isProducer );
|
||||
timeStepAndCalculatorPairs.emplace( allTimeSteps[i], calculator );
|
||||
}
|
||||
else if ( pipeBranchesCLCoords.size() > 0 )
|
||||
{
|
||||
const auto calculator =
|
||||
RigAccWellFlowCalculator( pipeBranchesCLCoords, pipeBranchesCellIds, smallContributionThreshold );
|
||||
// NOTE: Would like to prevent this check. Is added due to calculator.tracerNames() gives
|
||||
// "oil", "water" and "gas" as return value when calculator.totalTracerFractions().size() = 0
|
||||
if ( calculator.totalTracerFractions().size() > 0 )
|
||||
{
|
||||
timeStepAndCalculatorPairs.emplace( allTimeSteps[i], calculator );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create collection
|
||||
RimWellAllocationOverTimeCollection collection( selectedTimeSteps, timeStepAndCalculatorPairs );
|
||||
|
||||
if ( m_flowValueType == FlowValueType::FLOW_RATE_PERCENTAGE )
|
||||
{
|
||||
collection.fillWithFlowRatePercentageValues();
|
||||
}
|
||||
else if ( m_flowValueType == FlowValueType::FLOW_RATE )
|
||||
{
|
||||
collection.fillWithFlowRateValues();
|
||||
}
|
||||
else if ( m_flowValueType == FlowValueType::FLOW_VOLUME )
|
||||
{
|
||||
collection.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 );
|
||||
}
|
||||
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 );
|
||||
}
|
||||
else
|
||||
{
|
||||
CAF_ASSERT( "Not handled FlowValue type!" );
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
caf::PdmFieldHandle* RimWellAllocationOverTimePlot::userDescriptionField()
|
||||
{
|
||||
return &m_userName;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimePlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
|
||||
{
|
||||
RimPlot::defineUiOrdering( uiConfigName, uiOrdering );
|
||||
|
||||
uiOrdering.add( &m_userName );
|
||||
uiOrdering.add( &m_showPlotTitle );
|
||||
|
||||
caf::PdmUiGroup& dataGroup = *uiOrdering.addNewGroup( "Plot Data" );
|
||||
dataGroup.add( &m_case );
|
||||
dataGroup.add( &m_wellName );
|
||||
|
||||
caf::PdmUiGroup& timeStepGroup = *uiOrdering.addNewGroup( "Time Step" );
|
||||
timeStepGroup.add( &m_selectedFromTimeStep );
|
||||
timeStepGroup.add( &m_selectedToTimeStep );
|
||||
caf::PdmUiGroup& excludeTimeStepGroup = *timeStepGroup.addNewGroup( "Exclude Time Steps" );
|
||||
excludeTimeStepGroup.add( &m_excludeTimeSteps );
|
||||
excludeTimeStepGroup.add( &m_applyExcludeTimeSteps );
|
||||
excludeTimeStepGroup.setCollapsedByDefault();
|
||||
|
||||
caf::PdmUiGroup& optionGroup = *uiOrdering.addNewGroup( "Options" );
|
||||
optionGroup.add( &m_flowDiagSolution );
|
||||
optionGroup.add( &m_flowValueType );
|
||||
optionGroup.add( &m_groupSmallContributions );
|
||||
optionGroup.add( &m_smallContributionsThreshold );
|
||||
m_smallContributionsThreshold.uiCapability()->setUiReadOnly( !m_groupSmallContributions() );
|
||||
|
||||
uiOrdering.skipRemainingFields( true );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimePlot::defineEditorAttribute( const caf::PdmFieldHandle* field,
|
||||
QString uiConfigName,
|
||||
caf::PdmUiEditorAttribute* attribute )
|
||||
{
|
||||
if ( field == &m_applyExcludeTimeSteps )
|
||||
{
|
||||
caf::PdmUiPushButtonEditorAttribute* attrib = dynamic_cast<caf::PdmUiPushButtonEditorAttribute*>( attribute );
|
||||
if ( attrib )
|
||||
{
|
||||
attrib->m_buttonText = "Apply";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimePlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
|
||||
const QVariant& oldValue,
|
||||
const QVariant& newValue )
|
||||
{
|
||||
RimPlot::fieldChangedByUi( changedField, oldValue, newValue );
|
||||
|
||||
if ( changedField == &m_case )
|
||||
{
|
||||
if ( m_flowDiagSolution && m_case )
|
||||
{
|
||||
m_flowDiagSolution = m_case->defaultFlowDiagSolution();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flowDiagSolution = nullptr;
|
||||
}
|
||||
|
||||
std::set<QString> sortedWellNames = findSortedWellNames();
|
||||
if ( !sortedWellNames.size() )
|
||||
m_wellName = "";
|
||||
else if ( sortedWellNames.count( m_wellName() ) == 0 )
|
||||
{
|
||||
m_wellName = *sortedWellNames.begin();
|
||||
}
|
||||
|
||||
setValidTimeStepRangeForCase();
|
||||
onLoadDataAndUpdate();
|
||||
}
|
||||
else if ( changedField == &m_wellName || changedField == &m_flowDiagSolution || changedField == &m_flowValueType ||
|
||||
changedField == &m_groupSmallContributions || changedField == &m_smallContributionsThreshold ||
|
||||
changedField == &m_selectedFromTimeStep || changedField == &m_selectedToTimeStep ||
|
||||
changedField == &m_applyExcludeTimeSteps )
|
||||
{
|
||||
onLoadDataAndUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::set<QString> RimWellAllocationOverTimePlot::findSortedWellNames()
|
||||
{
|
||||
if ( m_case && m_case->eclipseCaseData() )
|
||||
{
|
||||
return m_case->eclipseCaseData()->findSortedWellNames();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QList<caf::PdmOptionItemInfo>
|
||||
RimWellAllocationOverTimePlot::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
|
||||
{
|
||||
QList<caf::PdmOptionItemInfo> options = RimPlot::calculateValueOptions( fieldNeedingOptions );
|
||||
if ( !options.empty() )
|
||||
{
|
||||
return options;
|
||||
}
|
||||
|
||||
if ( fieldNeedingOptions == &m_case )
|
||||
{
|
||||
auto resultCases = RimEclipseCaseTools::eclipseResultCases();
|
||||
for ( RimEclipseResultCase* c : resultCases )
|
||||
{
|
||||
options.push_back( caf::PdmOptionItemInfo( c->caseUserDescription(), c, false, c->uiIconProvider() ) );
|
||||
}
|
||||
}
|
||||
else if ( fieldNeedingOptions == &m_wellName )
|
||||
{
|
||||
const std::set<QString> sortedWellNames = findSortedWellNames();
|
||||
caf::IconProvider simWellIcon( ":/Well.svg" );
|
||||
for ( const auto& name : sortedWellNames )
|
||||
{
|
||||
options.push_back( caf::PdmOptionItemInfo( name, name, false, simWellIcon ) );
|
||||
}
|
||||
if ( options.size() == 0 )
|
||||
{
|
||||
options.push_front( caf::PdmOptionItemInfo( "None", nullptr ) );
|
||||
}
|
||||
}
|
||||
else if ( fieldNeedingOptions == &m_flowDiagSolution && m_case )
|
||||
{
|
||||
RimFlowDiagSolution* defaultFlowSolution = m_case->defaultFlowDiagSolution();
|
||||
options.push_back( caf::PdmOptionItemInfo( "Well Flow", nullptr ) );
|
||||
if ( defaultFlowSolution )
|
||||
{
|
||||
options.push_back( caf::PdmOptionItemInfo( "Allocation", defaultFlowSolution ) );
|
||||
}
|
||||
}
|
||||
else if ( m_case && ( fieldNeedingOptions == &m_excludeTimeSteps || fieldNeedingOptions == &m_selectedFromTimeStep ||
|
||||
fieldNeedingOptions == &m_selectedToTimeStep ) )
|
||||
{
|
||||
const QString dateFormatStr = dateFormatString();
|
||||
const auto timeSteps = m_case->timeStepDates();
|
||||
for ( const auto& timeStep : timeSteps )
|
||||
{
|
||||
options.push_back( caf::PdmOptionItemInfo( timeStep.toString( dateFormatStr ), timeStep ) );
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Color3f RimWellAllocationOverTimePlot::getTracerColor( const QString& tracerName )
|
||||
{
|
||||
if ( tracerName == RIG_FLOW_OIL_NAME ) return cvf::Color3f::DARK_GREEN;
|
||||
if ( tracerName == RIG_FLOW_GAS_NAME ) return cvf::Color3f::DARK_RED;
|
||||
if ( tracerName == RIG_FLOW_WATER_NAME ) return cvf::Color3f::BLUE;
|
||||
return cvf::Color3f::DARK_GRAY;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RimWellAllocationOverTimePlot::getValueTypeText() const
|
||||
{
|
||||
RiaDefines::EclipseUnitSystem unitSet = m_case->eclipseCaseData()->unitsType();
|
||||
RimWellLogFile::WellFlowCondition condition = m_flowDiagSolution ? RimWellLogFile::WELL_FLOW_COND_RESERVOIR
|
||||
: RimWellLogFile::WELL_FLOW_COND_STANDARD;
|
||||
|
||||
if ( m_flowValueType == FlowValueType::FLOW_RATE_PERCENTAGE )
|
||||
{
|
||||
QString conditionText = condition == RimWellLogFile::WELL_FLOW_COND_RESERVOIR ? "Reservoir" : "Surface";
|
||||
return QString( "Percentage of %1 Flow Rate [%]" ).arg( conditionText );
|
||||
}
|
||||
if ( m_flowValueType == FlowValueType::FLOW_RATE )
|
||||
{
|
||||
return RimWellPlotTools::flowPlotAxisTitle( condition, unitSet );
|
||||
}
|
||||
if ( m_flowValueType == FlowValueType::FLOW_VOLUME )
|
||||
{
|
||||
return RimWellPlotTools::flowVolumePlotAxisTitle( condition, unitSet );
|
||||
}
|
||||
if ( m_flowValueType == FlowValueType::ACCUMULATED_FLOW_VOLUME )
|
||||
{
|
||||
return "Accumulated " + RimWellPlotTools::flowVolumePlotAxisTitle( condition, unitSet );
|
||||
}
|
||||
if ( m_flowValueType == FlowValueType::ACCUMULATED_FLOW_VOLUME_PERCENTAGE )
|
||||
{
|
||||
QString conditionText = condition == RimWellLogFile::WELL_FLOW_COND_RESERVOIR ? "Reservoir" : "Surface";
|
||||
return QString( "Accumulated %1 Flow Volume Allocation [%]" ).arg( conditionText );
|
||||
}
|
||||
|
||||
return QString( "" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RimWellAllocationOverTimePlot::dateFormatString() const
|
||||
{
|
||||
return RiaQDateTimeTools::dateFormatString( RiaPreferences::current()->dateFormat(),
|
||||
RiaDefines::DateFormatComponents::DATE_FORMAT_YEAR_MONTH_DAY );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Update selected "From Time Step" and "To Time Step" according to selected case.
|
||||
/// If both selected time steps exist for case, keep as is. Otherwise set the 10 last time steps
|
||||
/// for case. If less than 10 time steps exist, all are selected.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellAllocationOverTimePlot::setValidTimeStepRangeForCase()
|
||||
{
|
||||
if ( m_case == nullptr || m_case->timeStepDates().size() == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto isTimeStepInCase = [&]( const QDateTime timeStep ) -> bool {
|
||||
return std::find( m_case->timeStepDates().cbegin(), m_case->timeStepDates().cend(), timeStep ) !=
|
||||
m_case->timeStepDates().cend();
|
||||
};
|
||||
if ( m_selectedFromTimeStep().isValid() && isTimeStepInCase( m_selectedFromTimeStep() ) &&
|
||||
m_selectedToTimeStep().isValid() && isTimeStepInCase( m_selectedToTimeStep() ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int numTimeSteps = m_case->timeStepDates().size();
|
||||
m_selectedToTimeStep = m_case->timeStepDates().back();
|
||||
m_selectedFromTimeStep = m_case->timeStepDates().at( std::max( numTimeSteps - m_initialNumberOfTimeSteps, 0 ) );
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "RimPlot.h"
|
||||
|
||||
#include "cafPdmField.h"
|
||||
#include "cafPdmPtrField.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QPointer>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
class RigAccWellFlowCalculator;
|
||||
class RimEclipseResultCase;
|
||||
class RimFlowDiagSolution;
|
||||
class RimWellAllocationOverTimeCollection;
|
||||
class RimSimWellInView;
|
||||
class RiuPlotWidget;
|
||||
class RiuQwtPlotWidget;
|
||||
|
||||
namespace cvf
|
||||
{
|
||||
class Color3f;
|
||||
}
|
||||
|
||||
class RimWellAllocationOverTimePlot : public RimPlot
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
enum class FlowValueType
|
||||
{
|
||||
FLOW_RATE,
|
||||
FLOW_RATE_PERCENTAGE,
|
||||
FLOW_VOLUME,
|
||||
ACCUMULATED_FLOW_VOLUME,
|
||||
ACCUMULATED_FLOW_VOLUME_PERCENTAGE,
|
||||
};
|
||||
|
||||
public:
|
||||
RimWellAllocationOverTimePlot();
|
||||
~RimWellAllocationOverTimePlot() override;
|
||||
|
||||
void setDescription( const QString& description );
|
||||
void setFromSimulationWell( RimSimWellInView* simWell );
|
||||
|
||||
// RimPlot implementations
|
||||
RiuPlotWidget* plotWidget() override;
|
||||
void setAutoScaleXEnabled( bool enabled ) override{};
|
||||
void setAutoScaleYEnabled( bool enabled ) override{};
|
||||
void updateAxes() override{};
|
||||
void updateLegend() override{};
|
||||
QString asciiDataForPlotExport() const override;
|
||||
void reattachAllCurves() override{};
|
||||
void detachAllCurves() override{};
|
||||
|
||||
// RimPlotWindow implementations
|
||||
QString description() const override;
|
||||
|
||||
// RimViewWindow implementations
|
||||
QWidget* viewWidget() override;
|
||||
QImage snapshotWindowContent() override;
|
||||
void zoomAll() override{};
|
||||
|
||||
private:
|
||||
// RimPlot implementations
|
||||
RiuPlotWidget* doCreatePlotViewWidget( QWidget* mainWindowParent ) override;
|
||||
|
||||
// RimViewWindow implementations
|
||||
void deleteViewWidget() override;
|
||||
void onLoadDataAndUpdate() override;
|
||||
|
||||
// PDM methods
|
||||
caf::PdmFieldHandle* userDescriptionField() override;
|
||||
|
||||
private:
|
||||
void updateFromWell();
|
||||
RimWellAllocationOverTimeCollection createWellAllocationOverTimeCollection() 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;
|
||||
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
|
||||
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override;
|
||||
QString getValueTypeText() const;
|
||||
QString dateFormatString() const;
|
||||
|
||||
void setValidTimeStepRangeForCase();
|
||||
|
||||
private:
|
||||
caf::PdmField<QString> m_userName;
|
||||
caf::PdmPtrField<RimEclipseResultCase*> m_case;
|
||||
caf::PdmField<QString> m_wellName;
|
||||
|
||||
caf::PdmField<QDateTime> m_selectedFromTimeStep;
|
||||
caf::PdmField<QDateTime> m_selectedToTimeStep;
|
||||
caf::PdmField<std::vector<QDateTime>> m_excludeTimeSteps;
|
||||
caf::PdmField<bool> m_applyExcludeTimeSteps;
|
||||
|
||||
caf::PdmPtrField<RimFlowDiagSolution*> m_flowDiagSolution;
|
||||
caf::PdmField<caf::AppEnum<FlowValueType>> m_flowValueType;
|
||||
caf::PdmField<bool> m_groupSmallContributions;
|
||||
caf::PdmField<double> m_smallContributionsThreshold;
|
||||
|
||||
QPointer<RiuQwtPlotWidget> m_plotWidget;
|
||||
|
||||
const int m_initialNumberOfTimeSteps = 10;
|
||||
};
|
||||
@@ -42,6 +42,7 @@
|
||||
#include "RimTools.h"
|
||||
#include "RimTotalWellAllocationPlot.h"
|
||||
#include "RimWellAllocationPlotLegend.h"
|
||||
#include "RimWellAllocationTools.h"
|
||||
#include "RimWellFlowRateCurve.h"
|
||||
#include "RimWellLogCurveCommonDataSource.h"
|
||||
#include "RimWellLogFile.h"
|
||||
@@ -247,7 +248,6 @@ void RimWellAllocationPlot::updateFromWell()
|
||||
if ( !simWellData ) return;
|
||||
|
||||
// Set up the Accumulated Well Flow Calculator
|
||||
|
||||
std::vector<std::vector<cvf::Vec3d>> pipeBranchesCLCoords;
|
||||
std::vector<std::vector<RigWellResultPoint>> pipeBranchesCellIds;
|
||||
|
||||
@@ -259,7 +259,8 @@ void RimWellAllocationPlot::updateFromWell()
|
||||
pipeBranchesCLCoords,
|
||||
pipeBranchesCellIds );
|
||||
|
||||
std::map<QString, const std::vector<double>*> tracerFractionCellValues = findRelevantTracerCellFractions( simWellData );
|
||||
std::map<QString, const std::vector<double>*> tracerFractionCellValues =
|
||||
RimWellAllocationTools::findOrCreateRelevantTracerCellFractions( simWellData, m_flowDiagSolution, m_timeStep );
|
||||
|
||||
std::unique_ptr<RigAccWellFlowCalculator> wfCalculator;
|
||||
|
||||
@@ -438,47 +439,6 @@ void RimWellAllocationPlot::updateFromWell()
|
||||
if ( m_wellAllocationPlotWidget ) m_wellAllocationPlotWidget->updateGeometry();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::map<QString, const std::vector<double>*>
|
||||
RimWellAllocationPlot::findRelevantTracerCellFractions( const RigSimWellData* simWellData )
|
||||
{
|
||||
std::map<QString, const std::vector<double>*> tracerCellFractionValues;
|
||||
|
||||
if ( m_flowDiagSolution && simWellData->hasWellResult( m_timeStep ) )
|
||||
{
|
||||
RimFlowDiagSolution::TracerStatusType requestedTracerType = RimFlowDiagSolution::TracerStatusType::UNDEFINED;
|
||||
|
||||
const RiaDefines::WellProductionType prodType = simWellData->wellProductionType( m_timeStep );
|
||||
if ( prodType == RiaDefines::WellProductionType::PRODUCER ||
|
||||
prodType == RiaDefines::WellProductionType::UNDEFINED_PRODUCTION_TYPE )
|
||||
{
|
||||
requestedTracerType = RimFlowDiagSolution::TracerStatusType::INJECTOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
requestedTracerType = RimFlowDiagSolution::TracerStatusType::PRODUCER;
|
||||
}
|
||||
|
||||
std::vector<QString> tracerNames = m_flowDiagSolution->tracerNames();
|
||||
for ( const QString& tracerName : tracerNames )
|
||||
{
|
||||
if ( m_flowDiagSolution->tracerStatusInTimeStep( tracerName, m_timeStep ) == requestedTracerType )
|
||||
{
|
||||
RigFlowDiagResultAddress resAddr( RIG_FLD_CELL_FRACTION_RESNAME,
|
||||
RigFlowDiagResultAddress::PHASE_ALL,
|
||||
tracerName.toStdString() );
|
||||
const std::vector<double>* tracerCellFractions =
|
||||
m_flowDiagSolution->flowDiagResults()->resultValues( resAddr, m_timeStep );
|
||||
if ( tracerCellFractions ) tracerCellFractionValues[tracerName] = tracerCellFractions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tracerCellFractionValues;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -490,49 +450,6 @@ void RimWellAllocationPlot::updateWellFlowPlotXAxisTitle( RimWellLogTrack* plotT
|
||||
|
||||
QString axisTitle = RimWellPlotTools::flowPlotAxisTitle( condition, unitSet );
|
||||
plotTrack->setPropertyValueAxisTitle( axisTitle );
|
||||
|
||||
#if 0
|
||||
if (m_flowDiagSolution)
|
||||
{
|
||||
QString unitText;
|
||||
switch ( unitSet )
|
||||
{
|
||||
case RiaEclipseUnitTools::UNITS_METRIC:
|
||||
unitText = "[m<sup>3</sup>/day]";
|
||||
break;
|
||||
case RiaEclipseUnitTools::UNITS_FIELD:
|
||||
unitText = "[Brl/day]";
|
||||
break;
|
||||
case RiaEclipseUnitTools::UNITS_LAB:
|
||||
unitText = "[cm<sup>3</sup>/hr]";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
plotTrack->setXAxisTitle("Reservoir Flow Rate " + unitText);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString unitText;
|
||||
switch ( unitSet )
|
||||
{
|
||||
case RiaEclipseUnitTools::UNITS_METRIC:
|
||||
unitText = "[Liquid Sm<sup>3</sup>/day], [Gas kSm<sup>3</sup>/day]";
|
||||
break;
|
||||
case RiaEclipseUnitTools::UNITS_FIELD:
|
||||
unitText = "[Liquid BBL/day], [Gas BOE/day]";
|
||||
break;
|
||||
case RiaEclipseUnitTools::UNITS_LAB:
|
||||
unitText = "[cm<sup>3</sup>/hr]";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
plotTrack->setXAxisTitle("Surface Flow Rate " + unitText);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -866,18 +783,11 @@ void RimWellAllocationPlot::fieldChangedByUi( const caf::PdmFieldHandle* changed
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::set<QString> RimWellAllocationPlot::findSortedWellNames()
|
||||
{
|
||||
std::set<QString> sortedWellNames;
|
||||
if ( m_case && m_case->eclipseCaseData() )
|
||||
{
|
||||
const cvf::Collection<RigSimWellData>& simWellData = m_case->eclipseCaseData()->wellResults();
|
||||
|
||||
for ( size_t wIdx = 0; wIdx < simWellData.size(); ++wIdx )
|
||||
{
|
||||
sortedWellNames.insert( simWellData[wIdx]->m_wellName );
|
||||
}
|
||||
return m_case->eclipseCaseData()->findSortedWellNames();
|
||||
}
|
||||
|
||||
return sortedWellNames;
|
||||
return {};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -108,9 +108,6 @@ protected:
|
||||
|
||||
private:
|
||||
void updateFromWell();
|
||||
|
||||
std::map<QString, const std::vector<double>*> findRelevantTracerCellFractions( const RigSimWellData* simWellData );
|
||||
|
||||
void updateWellFlowPlotXAxisTitle( RimWellLogTrack* plotTrack );
|
||||
|
||||
void addStackedCurve( const QString& tracerName,
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2022- 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 "RimWellAllocationTools.h"
|
||||
|
||||
#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 )
|
||||
{
|
||||
std::map<QString, const std::vector<double>*> tracerCellFractionValues = {};
|
||||
if ( flowDiagSolution && simWellData->hasWellResult( timeStepIndex ) )
|
||||
{
|
||||
RimFlowDiagSolution::TracerStatusType requestedTracerType = RimFlowDiagSolution::TracerStatusType::UNDEFINED;
|
||||
|
||||
const RiaDefines::WellProductionType prodType = simWellData->wellProductionType( timeStepIndex );
|
||||
if ( prodType == RiaDefines::WellProductionType::PRODUCER ||
|
||||
prodType == RiaDefines::WellProductionType::UNDEFINED_PRODUCTION_TYPE )
|
||||
{
|
||||
requestedTracerType = RimFlowDiagSolution::TracerStatusType::INJECTOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
requestedTracerType = RimFlowDiagSolution::TracerStatusType::PRODUCER;
|
||||
}
|
||||
|
||||
std::vector<QString> tracerNames = flowDiagSolution->tracerNames();
|
||||
for ( const QString& tracerName : tracerNames )
|
||||
{
|
||||
if ( flowDiagSolution->tracerStatusInTimeStep( tracerName, timeStepIndex ) == requestedTracerType )
|
||||
{
|
||||
RigFlowDiagResultAddress resAddr( RIG_FLD_CELL_FRACTION_RESNAME,
|
||||
RigFlowDiagResultAddress::PHASE_ALL,
|
||||
tracerName.toStdString() );
|
||||
const std::vector<double>* tracerCellFractions =
|
||||
flowDiagSolution->flowDiagResults()->resultValues( resAddr, timeStepIndex );
|
||||
if ( tracerCellFractions )
|
||||
{
|
||||
tracerCellFractionValues[tracerName] = tracerCellFractions;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tracerCellFractionValues;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2022- 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>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class RigSimWellData;
|
||||
class RimFlowDiagSolution;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
namespace RimWellAllocationTools
|
||||
{
|
||||
std::map<QString, const std::vector<double>*> findOrCreateRelevantTracerCellFractions( const RigSimWellData* simWellData,
|
||||
RimFlowDiagSolution* flowDiagSolution,
|
||||
int timeStepIndex );
|
||||
}
|
||||
@@ -886,6 +886,88 @@ QString RimWellPlotTools::flowUnitText( RimWellLogFile::WellFlowCondition condit
|
||||
return unitText;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RimWellPlotTools::flowVolumePlotAxisTitle( RimWellLogFile::WellFlowCondition condition,
|
||||
RiaDefines::EclipseUnitSystem unitSystem )
|
||||
{
|
||||
QString axisTitle;
|
||||
|
||||
if ( condition == RimWellLogFile::WELL_FLOW_COND_RESERVOIR )
|
||||
{
|
||||
QString unitText = RimWellPlotTools::flowVolumeUnitText( condition, unitSystem );
|
||||
|
||||
axisTitle = "Reservoir Flow Volume " + unitText;
|
||||
}
|
||||
else
|
||||
{
|
||||
QString unitText = RimWellPlotTools::flowVolumeUnitText( condition, unitSystem );
|
||||
|
||||
axisTitle = "Surface Flow Volume " + unitText;
|
||||
}
|
||||
|
||||
return axisTitle;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString flowVolumeConditionReservoirUnitText( RiaDefines::EclipseUnitSystem unitSystem )
|
||||
{
|
||||
QString unitText;
|
||||
|
||||
switch ( unitSystem )
|
||||
{
|
||||
case RiaDefines::EclipseUnitSystem::UNITS_METRIC:
|
||||
unitText = "[m<sup>3</sup>]";
|
||||
break;
|
||||
case RiaDefines::EclipseUnitSystem::UNITS_FIELD:
|
||||
unitText = "[Brl]";
|
||||
break;
|
||||
case RiaDefines::EclipseUnitSystem::UNITS_LAB:
|
||||
unitText = "[cm<sup>3</sup>]";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return unitText;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RimWellPlotTools::flowVolumeUnitText( RimWellLogFile::WellFlowCondition condition,
|
||||
RiaDefines::EclipseUnitSystem unitSystem )
|
||||
{
|
||||
QString unitText;
|
||||
|
||||
if ( condition == RimWellLogFile::WELL_FLOW_COND_RESERVOIR )
|
||||
{
|
||||
unitText = flowVolumeConditionReservoirUnitText( unitSystem );
|
||||
}
|
||||
else
|
||||
{
|
||||
switch ( unitSystem )
|
||||
{
|
||||
case RiaDefines::EclipseUnitSystem::UNITS_METRIC:
|
||||
unitText = "[Liquid Sm<sup>3</sup>], [Gas kSm<sup>3</sup>]";
|
||||
break;
|
||||
case RiaDefines::EclipseUnitSystem::UNITS_FIELD:
|
||||
unitText = "[Liquid BBL], [Gas BOE]";
|
||||
break;
|
||||
case RiaDefines::EclipseUnitSystem::UNITS_LAB:
|
||||
unitText = "[cm<sup>3</sup>]";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return unitText;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -122,8 +122,11 @@ public:
|
||||
|
||||
static QString flowPlotAxisTitle( RimWellLogFile::WellFlowCondition condition,
|
||||
RiaDefines::EclipseUnitSystem unitSystem );
|
||||
|
||||
static QString flowUnitText( RimWellLogFile::WellFlowCondition condition, RiaDefines::EclipseUnitSystem unitSystem );
|
||||
static QString flowVolumePlotAxisTitle( RimWellLogFile::WellFlowCondition condition,
|
||||
RiaDefines::EclipseUnitSystem unitSystem );
|
||||
static QString flowVolumeUnitText( RimWellLogFile::WellFlowCondition condition,
|
||||
RiaDefines::EclipseUnitSystem unitSystem );
|
||||
|
||||
static QString curveUnitText( RimWellLogFile::WellFlowCondition condition,
|
||||
RiaDefines::EclipseUnitSystem unitSystem,
|
||||
|
||||
@@ -874,8 +874,6 @@ void RigAccWellFlowCalculator::groupSmallContributions()
|
||||
|
||||
std::vector<std::pair<QString, double>> totalTracerFractions = this->totalTracerFractions();
|
||||
|
||||
if ( totalTracerFractions.size() < 5 ) return; // No grouping for few legend items
|
||||
|
||||
for ( const auto& tracerPair : totalTracerFractions )
|
||||
{
|
||||
if ( fabs( tracerPair.second ) <= m_smallContributionsThreshold &&
|
||||
|
||||
Reference in New Issue
Block a user