diff --git a/ApplicationCode/ProjectDataModel/Flow/RimFlowPlotCollection.cpp b/ApplicationCode/ProjectDataModel/Flow/RimFlowPlotCollection.cpp index f837b1f4c3..aa769eabf1 100644 --- a/ApplicationCode/ProjectDataModel/Flow/RimFlowPlotCollection.cpp +++ b/ApplicationCode/ProjectDataModel/Flow/RimFlowPlotCollection.cpp @@ -23,6 +23,7 @@ #include "RimFlowCharacteristicsPlot.h" #include "RimProject.h" #include "RimWellAllocationPlot.h" +#include "RimWellDistributionPlot.h" #include "cafProgressInfo.h" #include "cvfAssert.h" @@ -42,6 +43,9 @@ RimFlowPlotCollection::RimFlowPlotCollection() CAF_PDM_InitFieldNoDefault( &m_defaultWellAllocPlot, "DefaultWellAllocationPlot", "", "", "", "" ); m_defaultWellAllocPlot.uiCapability()->setUiHidden( true ); + CAF_PDM_InitFieldNoDefault( &m_wellDistributionPlot, "WellDistributionPlot", "", "", "", "" ); + m_wellDistributionPlot.uiCapability()->setUiHidden( true ); + CAF_PDM_InitFieldNoDefault( &m_storedWellAllocPlots, "StoredWellAllocationPlots", "Stored Well Allocation Plots", @@ -88,7 +92,7 @@ void RimFlowPlotCollection::closeDefaultPlotWindowAndDeletePlots() //-------------------------------------------------------------------------------------------------- void RimFlowPlotCollection::loadDataAndUpdate() { - caf::ProgressInfo plotProgress( m_storedWellAllocPlots.size() + m_storedFlowCharacteristicsPlots.size() + 1, "" ); + caf::ProgressInfo plotProgress( m_storedWellAllocPlots.size() + m_storedFlowCharacteristicsPlots.size() + 2, "" ); if ( m_defaultWellAllocPlot ) m_defaultWellAllocPlot->loadDataAndUpdate(); plotProgress.incrementProgress(); @@ -109,6 +113,11 @@ void RimFlowPlotCollection::loadDataAndUpdate() { m_flowCharacteristicsPlot->loadDataAndUpdate(); } + + if ( m_wellDistributionPlot ) + { + m_wellDistributionPlot->loadDataAndUpdate(); + } } //-------------------------------------------------------------------------------------------------- @@ -185,4 +194,9 @@ void RimFlowPlotCollection::ensureDefaultFlowPlotsAreCreated() { m_flowCharacteristicsPlot = new RimFlowCharacteristicsPlot; } + + if ( !m_wellDistributionPlot() ) + { + m_wellDistributionPlot = new RimWellDistributionPlot; + } } diff --git a/ApplicationCode/ProjectDataModel/Flow/RimFlowPlotCollection.h b/ApplicationCode/ProjectDataModel/Flow/RimFlowPlotCollection.h index 07b6ae713f..f5fd25c818 100644 --- a/ApplicationCode/ProjectDataModel/Flow/RimFlowPlotCollection.h +++ b/ApplicationCode/ProjectDataModel/Flow/RimFlowPlotCollection.h @@ -24,6 +24,7 @@ class RimWellAllocationPlot; class RimFlowCharacteristicsPlot; +class RimWellDistributionPlot; //================================================================================================== /// @@ -50,6 +51,7 @@ public: private: caf::PdmChildField m_flowCharacteristicsPlot; caf::PdmChildField m_defaultWellAllocPlot; + caf::PdmChildField m_wellDistributionPlot; caf::PdmChildArrayField m_storedWellAllocPlots; caf::PdmChildArrayField m_storedFlowCharacteristicsPlots; }; diff --git a/ApplicationCode/ProjectDataModel/Flow/RimWellDistributionPlot.cpp b/ApplicationCode/ProjectDataModel/Flow/RimWellDistributionPlot.cpp index dad415177b..53c36daeb2 100644 --- a/ApplicationCode/ProjectDataModel/Flow/RimWellDistributionPlot.cpp +++ b/ApplicationCode/ProjectDataModel/Flow/RimWellDistributionPlot.cpp @@ -17,7 +17,28 @@ ///////////////////////////////////////////////////////////////////////////////// #include "RimWellDistributionPlot.h" +#include "RimEclipseResultCase.h" +#include "RimProject.h" +#include "RimFlowDiagSolution.h" +#include "RigEclipseCaseData.h" +#include "RigTofWellDistributionCalculator.h" + +#include "RiaColorTools.h" + +#include "RiuQwtPlotTools.h" + +#include "qwt_plot.h" +#include "qwt_legend.h" +#include "qwt_plot_curve.h" + +#include +#include +#include + +#include "cvfBase.h" +#include "cvfTrace.h" +#include "cvfDebugTimer.h" //================================================================================================== @@ -33,7 +54,17 @@ CAF_PDM_SOURCE_INIT(RimWellDistributionPlot, "WellDistributionPlot"); //-------------------------------------------------------------------------------------------------- RimWellDistributionPlot::RimWellDistributionPlot() { + cvf::Trace::show("RimWellDistributionPlot::RimWellDistributionPlot()"); + CAF_PDM_InitObject("Well Distribution Plot", "", "", ""); + + CAF_PDM_InitFieldNoDefault(&m_case, "Case", "Case", "", "", ""); + CAF_PDM_InitField(&m_timeStepIndex, "TimeStepIndex", -1, "Time Step", "", "", ""); + CAF_PDM_InitField(&m_wellName, "WellName", QString("None"), "Well", "", "", ""); + + m_showWindow = false; + + setAsPlotMdiWindow(); } //-------------------------------------------------------------------------------------------------- @@ -48,7 +79,8 @@ RimWellDistributionPlot::~RimWellDistributionPlot() //-------------------------------------------------------------------------------------------------- QWidget* RimWellDistributionPlot::viewWidget() { - return nullptr; + cvf::Trace::show("RimWellDistributionPlot::viewWidget()"); + return m_myViewWidget; } //-------------------------------------------------------------------------------------------------- @@ -56,6 +88,7 @@ QWidget* RimWellDistributionPlot::viewWidget() //-------------------------------------------------------------------------------------------------- QImage RimWellDistributionPlot::snapshotWindowContent() { + cvf::Trace::show("RimWellDistributionPlot::snapshotWindowContent()"); return QImage(); } @@ -64,103 +97,323 @@ QImage RimWellDistributionPlot::snapshotWindowContent() //-------------------------------------------------------------------------------------------------- void RimWellDistributionPlot::zoomAll() { + cvf::Trace::show("RimWellDistributionPlot::zoomAll()"); } -RiuQwtPlotWidget* RimWellDistributionPlot::viewer() -{ - throw std::logic_error("The method or operation is not implemented."); -} - -bool RimWellDistributionPlot::isChecked() const -{ - throw std::logic_error("The method or operation is not implemented."); -} - -void RimWellDistributionPlot::setChecked(bool checked) -{ - throw std::logic_error("The method or operation is not implemented."); -} - +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- QString RimWellDistributionPlot::description() const { - throw std::logic_error("The method or operation is not implemented."); -} - -bool RimWellDistributionPlot::hasCustomFontSizes(RiaDefines::FontSettingType fontSettingType, int defaultFontSize) const -{ - throw std::logic_error("The method or operation is not implemented."); -} - -bool RimWellDistributionPlot::applyFontSize(RiaDefines::FontSettingType fontSettingType, int oldFontSize, int fontSize, bool forceChange /*= false*/) -{ - throw std::logic_error("The method or operation is not implemented."); -} - -void RimWellDistributionPlot::setAutoScaleXEnabled(bool enabled) -{ - throw std::logic_error("The method or operation is not implemented."); -} - -void RimWellDistributionPlot::setAutoScaleYEnabled(bool enabled) -{ - throw std::logic_error("The method or operation is not implemented."); -} - -void RimWellDistributionPlot::updateAxes() -{ - throw std::logic_error("The method or operation is not implemented."); -} - -void RimWellDistributionPlot::updateZoomInQwt() -{ - throw std::logic_error("The method or operation is not implemented."); -} - -void RimWellDistributionPlot::updateZoomFromQwt() -{ - throw std::logic_error("The method or operation is not implemented."); -} - -void RimWellDistributionPlot::createPlotWidget() -{ - throw std::logic_error("The method or operation is not implemented."); -} - -void RimWellDistributionPlot::detachAllCurves() -{ - throw std::logic_error("The method or operation is not implemented."); -} - -caf::PdmObject* RimWellDistributionPlot::findPdmObjectFromQwtCurve(const QwtPlotCurve* curve) const -{ - throw std::logic_error("The method or operation is not implemented."); -} - -void RimWellDistributionPlot::loadDataAndUpdate() -{ - -} - -void RimWellDistributionPlot::updateLayout() -{ - throw std::logic_error("The method or operation is not implemented."); + return uiName(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- QWidget* RimWellDistributionPlot::createViewWidget(QWidget* mainWindowParent) { - return nullptr; + cvf::Trace::show("RimWellDistributionPlot::createViewWidget()"); + + m_myViewWidget = new QWidget(mainWindowParent); + + QGridLayout* gridLayout = new QGridLayout(m_myViewWidget); + + m_textBrowser = new QTextBrowser; + m_textBrowser->setText("

NotYet

"); + gridLayout->addWidget(m_textBrowser, 0, 0); + + m_plotWidgets[0] = constructNewPlotWidget(); + m_plotWidgets[1] = constructNewPlotWidget(); + m_plotWidgets[2] = constructNewPlotWidget(); + + gridLayout->addWidget(m_plotWidgets[0], 1, 0); + gridLayout->addWidget(m_plotWidgets[1], 0, 1); + gridLayout->addWidget(m_plotWidgets[2], 1, 1); + + onLoadDataAndUpdate(); + + return m_myViewWidget; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QwtPlot* RimWellDistributionPlot::constructNewPlotWidget() +{ + class MyQwtPlot : public QwtPlot + { + public: + MyQwtPlot(QWidget* parent) : QwtPlot(parent) {} + QSize sizeHint() const override { return QSize(100, 100); } + QSize minimumSizeHint() const override { return QSize(0, 0); } + }; + + QwtPlot* plotWidget = new MyQwtPlot(nullptr); + RiuQwtPlotTools::setCommonPlotBehaviour(plotWidget); + + QwtLegend* legend = new QwtLegend(plotWidget); + plotWidget->insertLegend(legend, QwtPlot::BottomLegend); + + return plotWidget; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- void RimWellDistributionPlot::deleteViewWidget() { + cvf::Trace::show("RimWellDistributionPlot::deleteViewWidget()"); + + if (m_myViewWidget) + { + m_myViewWidget->deleteLater(); + m_myViewWidget = nullptr; + } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellDistributionPlot::populatePlotWidgetWithCurveData(const RigTofWellDistributionCalculator& calculator, RiaDefines::PhaseType phase, const RimFlowDiagSolution& flowDiagSolution, QwtPlot* plotWidget) +{ + cvf::Trace::show("RimWellDistributionPlot::populatePlotWidgetWithCurves(phase=%d '%s')", phase, phase == RiaDefines::OIL_PHASE ? "oil" : (phase == RiaDefines::GAS_PHASE ? "gas" : "water")); + + // Currently select this value so that the grid appears on top of the curves + const double baseCurveZValue = 9.5; + + plotWidget->detachItems(QwtPlotItem::Rtti_PlotCurve); + plotWidget->setAxisScale(QwtPlot::xBottom, 0, 1); + plotWidget->setAxisScale(QwtPlot::yLeft, 0, 1); + plotWidget->setAxisAutoScale(QwtPlot::xBottom, true); + plotWidget->setAxisAutoScale(QwtPlot::yLeft, true); + + const std::vector& tofValuesDays = calculator.sortedUniqueTOFValues(); + if (tofValuesDays.size() == 0) + { + cvf::Trace::show("No TOF values!"); + return; + } + + std::vector tofValuesYears; + for (double tofDays : tofValuesDays) + { + const double tofYears = tofDays/365.2425; + tofValuesYears.push_back(tofYears); + } + + cvf::Trace::show("numTofValues: %d (min, max: %f, %f)", static_cast(tofValuesYears.size()), tofValuesYears.front(), tofValuesYears.back()); + + const size_t numWells = calculator.contributingWellCount(); + cvf::Trace::show("numContribWells: %d", static_cast(numWells)); + + std::vector yVals(tofValuesYears.size(), 0); + + for (size_t i = 0; i < numWells; i++) + { + QString wellName = calculator.contributingWellName(i); + const std::vector& volArr = calculator.accumulatedPhaseVolumeForContributingWell(phase, i); + + cvf::Color3f cvfClr = flowDiagSolution.tracerColor(wellName); + QColor qtClr = RiaColorTools::toQColor(cvfClr); + + cvf::Trace::show("wellName min, max: %s %f, %f", wellName.toStdString().c_str(), volArr.front(), volArr.back()); + + for (size_t j = 0; j < yVals.size(); j++) + { + yVals[j] += volArr[j]; + } + + QwtPlotCurve* curve = new QwtPlotCurve; + curve->setTitle(wellName); + curve->setBrush(qtClr); + curve->setZ(baseCurveZValue - i * 0.0001); + curve->setSamples(tofValuesYears.data(), yVals.data(), static_cast(tofValuesYears.size())); + curve->attach(plotWidget); + } +} + + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- void RimWellDistributionPlot::onLoadDataAndUpdate() { + cvf::Trace::show("RimWellDistributionPlot::onLoadDataAndUpdate()"); + cvf::DebugTimer tim("RimWellDistributionPlot::onLoadDataAndUpdate()"); + + updateMdiWindowVisibility(); + + if (!m_myViewWidget) + { + return; + } + + CVF_ASSERT(m_textBrowser); + + QString str = "

MyViewWidget

"; + str += "

"; + + const QString caseName = m_case ? m_case->caseUserDescription() : "N/A"; + + str += QString("case: %1
").arg(caseName); + str += QString("timeStepIndex: %1
").arg(m_timeStepIndex); + str += QString("wellName: %1
").arg(m_wellName); + + m_textBrowser->setText(str); + + + for (QwtPlot* plotWidget : m_plotWidgets) + { + plotWidget->detachItems(QwtPlotItem::Rtti_PlotCurve); + } + + if (m_case) + { + tim.reportLapTimeMS("about to start calc"); + RigTofWellDistributionCalculator calc(m_case, m_wellName, m_timeStepIndex); + tim.reportLapTimeMS("calc"); + + const QString timeStepName = m_case->timeStepName(m_timeStepIndex); + + const RimFlowDiagSolution* flowDiagSolution = m_case->defaultFlowDiagSolution(); + for (int i = 0; i < m_plotWidgets.size(); i++) + { + const RiaDefines::PhaseType phase = static_cast(i); + + QwtPlot* plotWidget = m_plotWidgets[i]; + populatePlotWidgetWithCurveData(calc, phase, *flowDiagSolution, plotWidget); + + QString phaseString = "N/A"; + if (phase == RiaDefines::OIL_PHASE) phaseString = "Oil"; + else if (phase == RiaDefines::GAS_PHASE) phaseString = "Gas"; + else if (phase == RiaDefines::WATER_PHASE) phaseString = "Water"; + + plotWidget->setAxisTitle(QwtPlot::xBottom, "TOF [years]"); + plotWidget->setAxisTitle(QwtPlot::yLeft, "Reservoir Volume [m3]"); + + const QString plotTitle = QString("%1 Distribution: %2, %3").arg(phaseString).arg(m_wellName).arg(timeStepName); + plotWidget->setTitle(plotTitle); + } + } + + for (QwtPlot* plotWidget : m_plotWidgets) + { + plotWidget->replot(); + } } -void RimWellDistributionPlot::updatePlotTitle() +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellDistributionPlot::defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) { - throw std::logic_error("The method or operation is not implemented."); + caf::PdmUiGroup* group = uiOrdering.addNewGroup("Plot Data"); + group->add(&m_case); + group->add(&m_timeStepIndex); + group->add(&m_wellName); + + uiOrdering.skipRemainingFields(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QList RimWellDistributionPlot::calculateValueOptions(const caf::PdmFieldHandle* fieldNeedingOptions, bool* /*useOptionsOnly*/) +{ + QList options; + + if (fieldNeedingOptions == &m_case) + { + RimProject* ownerProj = nullptr; + firstAncestorOrThisOfType(ownerProj); + if (ownerProj) + { + std::vector caseArr; + ownerProj->descendantsIncludingThisOfType(caseArr); + for (RimEclipseResultCase* c : caseArr) + { + options.push_back(caf::PdmOptionItemInfo(c->caseUserDescription(), c, true, c->uiIconProvider())); + } + } + } + + else if (fieldNeedingOptions == &m_timeStepIndex) + { + if (m_case && m_case->eclipseCaseData()) + { + const QStringList timeStepNames = m_case->timeStepStrings(); + for (int i = 0; i < timeStepNames.size(); i++) + { + options.push_back(caf::PdmOptionItemInfo(timeStepNames[i], i)); + } + } + + if (options.size() == 0) + { + options.push_back(caf::PdmOptionItemInfo("None", -1)); + } + } + + else if (fieldNeedingOptions == &m_wellName) + { + if (m_case && m_case->eclipseCaseData()) + { + caf::QIconProvider simWellIcon(":/Well.png"); + const std::set sortedWellNameSet = m_case->eclipseCaseData()->findSortedWellNames(); + for (const QString& name : sortedWellNameSet) + { + options.push_back(caf::PdmOptionItemInfo(name, name, true, simWellIcon)); + } + } + + if (options.size() == 0) + { + options.push_back(caf::PdmOptionItemInfo("None", QVariant())); + } + } + + return options; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellDistributionPlot::fieldChangedByUi(const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue) +{ + if (changedField == &m_case) + { + fixupDependentFieldsAfterCaseChange(); + } + + onLoadDataAndUpdate(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellDistributionPlot::fixupDependentFieldsAfterCaseChange() +{ + int newTimeStepIndex = -1; + QString newWellName; + + if (m_case) + { + const int timeStepCount = m_case->timeStepStrings().size(); + if (timeStepCount > 0) + { + newTimeStepIndex = timeStepCount - 1; + } + + const std::set sortedWellNameSet = m_case->eclipseCaseData()->findSortedWellNames(); + if (sortedWellNameSet.size() > 0) + { + newWellName = *sortedWellNameSet.begin(); + } + } + + m_timeStepIndex = newTimeStepIndex; + m_wellName = newWellName; +} + + diff --git a/ApplicationCode/ProjectDataModel/Flow/RimWellDistributionPlot.h b/ApplicationCode/ProjectDataModel/Flow/RimWellDistributionPlot.h index 810a9cb043..b52cee2908 100644 --- a/ApplicationCode/ProjectDataModel/Flow/RimWellDistributionPlot.h +++ b/ApplicationCode/ProjectDataModel/Flow/RimWellDistributionPlot.h @@ -19,7 +19,20 @@ #pragma once #include "RimPlotWindow.h" -#include "RimPlotInterface.h" +#include "RiaDefines.h" + +#include "cafPdmPtrField.h" + +#include + +#include + +class RimEclipseResultCase; +class RimFlowDiagSolution; +class RigTofWellDistributionCalculator; + +class QTextBrowser; +class QwtPlot; //================================================================================================== @@ -27,7 +40,7 @@ // // //================================================================================================== -class RimWellDistributionPlot : public RimPlotWindow, public RimPlotInterface +class RimWellDistributionPlot : public RimPlotWindow { CAF_PDM_HEADER_INIT; @@ -35,34 +48,37 @@ public: RimWellDistributionPlot(); ~RimWellDistributionPlot() override; - // RimViewWindow/RimPlotWindow implementations/overrides - virtual QWidget* viewWidget() override; - virtual QImage snapshotWindowContent() override; - virtual void zoomAll() override; - virtual void detachAllCurves() override; - virtual void updateLayout() override; + // RimPlotWindow implementations + virtual QString description() const override; - // RimPlotInterface implementation - virtual RiuQwtPlotWidget* viewer() override; - virtual bool isChecked() const override; - virtual void setChecked(bool checked) override; - virtual QString description() const override; - virtual bool hasCustomFontSizes(RiaDefines::FontSettingType fontSettingType, int defaultFontSize) const override; - virtual bool applyFontSize(RiaDefines::FontSettingType fontSettingType, int oldFontSize, int fontSize, bool forceChange = false) override; - virtual void setAutoScaleXEnabled(bool enabled) override; - virtual void setAutoScaleYEnabled(bool enabled) override; - virtual void updateAxes() override; - virtual void updateZoomInQwt() override; - virtual void updateZoomFromQwt() override; - virtual void createPlotWidget() override; - //virtual void detachAllCurves() override; - virtual caf::PdmObject* findPdmObjectFromQwtCurve(const QwtPlotCurve* curve) const override; - virtual void loadDataAndUpdate() override; + // RimViewWindow implementations + virtual QWidget* viewWidget() override; + virtual QImage snapshotWindowContent() override; + virtual void zoomAll() override; private: - // RimViewWindow/RimPlotWindow implementations/overrides + // RimPlotWindow overrides + virtual QList calculateValueOptions(const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly) override; + virtual void fieldChangedByUi(const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue) override; + + // RimViewWindow implementations virtual QWidget* createViewWidget(QWidget* mainWindowParent) override; virtual void deleteViewWidget() override; virtual void onLoadDataAndUpdate() override; - virtual void updatePlotTitle() override; + +private: + virtual void defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) override; + void fixupDependentFieldsAfterCaseChange(); + static QwtPlot* constructNewPlotWidget(); + static void populatePlotWidgetWithCurveData(const RigTofWellDistributionCalculator& calculator, RiaDefines::PhaseType phase, const RimFlowDiagSolution& flowDiagSolution, QwtPlot* plotWidget); + + +private: + caf::PdmPtrField m_case; + caf::PdmField m_timeStepIndex; + caf::PdmField m_wellName; + + QPointer m_myViewWidget; + QPointer m_textBrowser; + std::array, 3> m_plotWidgets; }; diff --git a/ApplicationCode/ReservoirDataModel/CMakeLists_files.cmake b/ApplicationCode/ReservoirDataModel/CMakeLists_files.cmake index c8ede2cc1b..010f177fdf 100644 --- a/ApplicationCode/ReservoirDataModel/CMakeLists_files.cmake +++ b/ApplicationCode/ReservoirDataModel/CMakeLists_files.cmake @@ -54,6 +54,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RigWellPathIntersectionTools.h ${CMAKE_CURRENT_LIST_DIR}/RigEclipseResultInfo.h ${CMAKE_CURRENT_LIST_DIR}/RigEclipseResultAddress.h ${CMAKE_CURRENT_LIST_DIR}/RigTofAccumulatedPhaseFractionsCalculator.h +${CMAKE_CURRENT_LIST_DIR}/RigTofWellDistributionCalculator.h ${CMAKE_CURRENT_LIST_DIR}/RigTransmissibilityEquations.h ${CMAKE_CURRENT_LIST_DIR}/RigNumberOfFloodedPoreVolumesCalculator.h ${CMAKE_CURRENT_LIST_DIR}/RigWeightedMeanCalc.h @@ -123,6 +124,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RigCellGeometryTools.cpp ${CMAKE_CURRENT_LIST_DIR}/RigWellPathIntersectionTools.cpp ${CMAKE_CURRENT_LIST_DIR}/RigEclipseResultInfo.cpp ${CMAKE_CURRENT_LIST_DIR}/RigTofAccumulatedPhaseFractionsCalculator.cpp +${CMAKE_CURRENT_LIST_DIR}/RigTofWellDistributionCalculator.cpp ${CMAKE_CURRENT_LIST_DIR}/RigTransmissibilityEquations.cpp ${CMAKE_CURRENT_LIST_DIR}/RigNumberOfFloodedPoreVolumesCalculator.cpp ${CMAKE_CURRENT_LIST_DIR}/RigWeightedMeanCalc.cpp diff --git a/ApplicationCode/ReservoirDataModel/RigTofWellDistributionCalculator.cpp b/ApplicationCode/ReservoirDataModel/RigTofWellDistributionCalculator.cpp new file mode 100644 index 0000000000..9686940885 --- /dev/null +++ b/ApplicationCode/ReservoirDataModel/RigTofWellDistributionCalculator.cpp @@ -0,0 +1,252 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RigTofWellDistributionCalculator.h" + +#include "RiaDefines.h" +#include "RiaPorosityModel.h" +#include "RiaLogging.h" + +#include "RigCaseCellResultsData.h" +#include "RigEclipseCaseData.h" +#include "RigFlowDiagResultAddress.h" +#include "RigFlowDiagResults.h" +#include "RigResultAccessor.h" +#include "RigResultAccessorFactory.h" + +#include "RimEclipseResultCase.h" +#include "RimFlowDiagSolution.h" +#include "RimReservoirCellResultsStorage.h" + +#include "cvfTrace.h" + +#include + + + +//================================================================================================== +// +// +// +//================================================================================================== + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RigTofWellDistributionCalculator::RigTofWellDistributionCalculator(RimEclipseResultCase* caseToApply, QString targetWellname, size_t timeStepIndex) +{ + CVF_ASSERT(caseToApply); + + RigEclipseCaseData* eclipseCaseData = caseToApply->eclipseCaseData(); + CVF_ASSERT(eclipseCaseData); + + RimFlowDiagSolution* flowDiagSolution = caseToApply->defaultFlowDiagSolution(); + CVF_ASSERT(flowDiagSolution); + + RigFlowDiagResults* flowDiagResults = flowDiagSolution->flowDiagResults(); + CVF_ASSERT(flowDiagResults); + + const std::vector* porvResults = eclipseCaseData->resultValues(RiaDefines::MATRIX_MODEL, RiaDefines::STATIC_NATIVE, "PORV", 0); + const std::vector* swatResults = eclipseCaseData->resultValues( RiaDefines::MATRIX_MODEL, RiaDefines::DYNAMIC_NATIVE, "SWAT", timeStepIndex); + const std::vector* soilResults = eclipseCaseData->resultValues( RiaDefines::MATRIX_MODEL, RiaDefines::DYNAMIC_NATIVE, "SOIL", timeStepIndex); + const std::vector* sgasResults = eclipseCaseData->resultValues( RiaDefines::MATRIX_MODEL, RiaDefines::DYNAMIC_NATIVE, "SGAS", timeStepIndex); + if (!porvResults) + { + return; + } + + const RigFlowDiagResultAddress resultAddrTof("TOF", RigFlowDiagResultAddress::PhaseSelection::PHASE_ALL, targetWellname.toStdString()); + const RigFlowDiagResultAddress resultAddrFraction("Fraction", RigFlowDiagResultAddress::PhaseSelection::PHASE_ALL, targetWellname.toStdString()); + const std::vector* tofData = flowDiagResults->resultValues(resultAddrTof, timeStepIndex); + const std::vector* targetWellFractionData = flowDiagResults->resultValues(resultAddrFraction, timeStepIndex); + if (!tofData || !targetWellFractionData) + { + return; + } + + + const std::map> tofToCellIndicesMap = buildSortedTofToCellIndicesMap(*tofData); + + const std::vector candidateContributingWellNames = findCandidateContributingWellNames(*flowDiagSolution, targetWellname, timeStepIndex); + const size_t numContribWells = candidateContributingWellNames.size(); + + for (size_t iContribWell = 0; iContribWell < numContribWells; iContribWell++) + { + const QString contribWellName = candidateContributingWellNames[iContribWell]; + + const RigFlowDiagResultAddress resultAddrContribWellFraction("Fraction", RigFlowDiagResultAddress::PhaseSelection::PHASE_ALL, contribWellName.toStdString()); + const std::vector* contribWellFractionData = flowDiagResults->resultValues(resultAddrContribWellFraction, timeStepIndex); + if (!contribWellFractionData) + { + continue; + } + + double accumulatedVol_wat = 0; + double accumulatedVol_oil = 0; + double accumulatedVol_gas = 0; + + ContribWellEntry contribWellEntry; + contribWellEntry.name = contribWellName; + + for (auto mapElement : tofToCellIndicesMap) + { + const double tofValue = mapElement.first; + const std::vector& cellIndicesArr = mapElement.second; + + for (size_t cellIndex : cellIndicesArr) + { + const double porv = porvResults->at(cellIndex); + const double targetWellFractionVal = targetWellFractionData->at(cellIndex); + const double contribWellFractionVal = contribWellFractionData->at(cellIndex); + if (contribWellFractionVal == HUGE_VAL) + { + continue; + } + + const double volAllPhasesThisCell = porv * targetWellFractionVal*contribWellFractionVal; + + if (swatResults) accumulatedVol_wat += swatResults->at(cellIndex)*volAllPhasesThisCell; + if (soilResults) accumulatedVol_oil += soilResults->at(cellIndex)*volAllPhasesThisCell; + if (sgasResults) accumulatedVol_gas += sgasResults->at(cellIndex)*volAllPhasesThisCell; + } + + contribWellEntry.accumulatedVolAlongTof_wat.push_back(accumulatedVol_wat); + contribWellEntry.accumulatedVolAlongTof_oil.push_back(accumulatedVol_oil); + contribWellEntry.accumulatedVolAlongTof_gas.push_back(accumulatedVol_gas); + } + + m_contributingWells.push_back(contribWellEntry); + } + + for (auto mapElement : tofToCellIndicesMap) + { + const double tofValue = mapElement.first; + m_tofInIncreasingOrder.push_back(tofValue); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::map> RigTofWellDistributionCalculator::buildSortedTofToCellIndicesMap(const std::vector& tofData) +{ + std::map> tofToCellIndicesMap; + + for (size_t i = 0; i < tofData.size(); i++) + { + const double tofValue = tofData[i]; + if (tofValue == HUGE_VAL) + { + continue; + } + + std::vector vectorOfIndexes{ i }; + auto iteratorBoolFromInsertToMap = tofToCellIndicesMap.insert(std::make_pair(tofValue, vectorOfIndexes)); + if (!iteratorBoolFromInsertToMap.second) + { + // Map element for this tofValue already exist => we must add the cell index ourselves + iteratorBoolFromInsertToMap.first->second.push_back(i); + } + } + + return tofToCellIndicesMap; +} + +//-------------------------------------------------------------------------------------------------- +/// Determine name of the the wells that are candidates for contributing in our calculation +//-------------------------------------------------------------------------------------------------- +std::vector RigTofWellDistributionCalculator::findCandidateContributingWellNames(const RimFlowDiagSolution& flowDiagSolution, QString targetWellname, size_t timeStepIndex) +{ + std::vector candidateWellNames; + + const RimFlowDiagSolution::TracerStatusType targetWellStatus = flowDiagSolution.tracerStatusInTimeStep(targetWellname, timeStepIndex); + if (targetWellStatus != RimFlowDiagSolution::INJECTOR && + targetWellStatus != RimFlowDiagSolution::PRODUCER) + { + RiaLogging::warning("Status of target well is neither INJECTOR nor PRODUCER"); + return candidateWellNames; + } + + const RimFlowDiagSolution::TracerStatusType oppositeStatus = (targetWellStatus == RimFlowDiagSolution::INJECTOR) ? RimFlowDiagSolution::PRODUCER : RimFlowDiagSolution::INJECTOR; + + const std::vector allWellNames = flowDiagSolution.tracerNames(); + for (QString name : allWellNames) + { + const RimFlowDiagSolution::TracerStatusType status = flowDiagSolution.tracerStatusInTimeStep(name, timeStepIndex); + if (status == oppositeStatus) + { + candidateWellNames.push_back(name); + } + else if (status == targetWellStatus) + { + if (RimFlowDiagSolution::hasCrossFlowEnding(name)) + { + candidateWellNames.push_back(name); + } + } + } + + return candidateWellNames; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const std::vector& RigTofWellDistributionCalculator::sortedUniqueTOFValues() const +{ + return m_tofInIncreasingOrder; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +size_t RigTofWellDistributionCalculator::contributingWellCount() const +{ + return m_contributingWells.size(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const QString& RigTofWellDistributionCalculator::contributingWellName(size_t contribWellIndex) const +{ + return m_contributingWells[contribWellIndex].name; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const std::vector& RigTofWellDistributionCalculator::accumulatedPhaseVolumeForContributingWell(RiaDefines::PhaseType phase, size_t contributingWellIndex) const +{ + CVF_ASSERT(contributingWellIndex < m_contributingWells.size()); + const ContribWellEntry& entry = m_contributingWells[contributingWellIndex]; + + if (phase == RiaDefines::WATER_PHASE) + { + return entry.accumulatedVolAlongTof_wat; + } + else if (phase == RiaDefines::OIL_PHASE) + { + return entry.accumulatedVolAlongTof_oil; + } + else + { + return entry.accumulatedVolAlongTof_gas; + } +} + diff --git a/ApplicationCode/ReservoirDataModel/RigTofWellDistributionCalculator.h b/ApplicationCode/ReservoirDataModel/RigTofWellDistributionCalculator.h new file mode 100644 index 0000000000..bb8343643c --- /dev/null +++ b/ApplicationCode/ReservoirDataModel/RigTofWellDistributionCalculator.h @@ -0,0 +1,66 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2019- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "RiaDefines.h" + +#include +#include +#include + +#include + +class RimEclipseResultCase; +class RimFlowDiagSolution; +class QString; + + +//================================================================================================== +// +// +// +//================================================================================================== +class RigTofWellDistributionCalculator +{ +public: + RigTofWellDistributionCalculator(RimEclipseResultCase* caseToApply, QString targetWellname, size_t timeStepIndex); + + const std::vector& sortedUniqueTOFValues() const; + + size_t contributingWellCount() const; + const QString& contributingWellName(size_t contribWellIndex) const; + const std::vector& accumulatedPhaseVolumeForContributingWell(RiaDefines::PhaseType phase, size_t contributingWellIndex) const; + +private: + static std::map> buildSortedTofToCellIndicesMap(const std::vector& tofData); + static std::vector findCandidateContributingWellNames(const RimFlowDiagSolution& flowDiagSolution, QString targetWellname, size_t timeStepIndex); + + struct ContribWellEntry + { + QString name; + std::vector accumulatedVolAlongTof_wat; // All these three have same size as m_tofInIncreasingOrder + std::vector accumulatedVolAlongTof_oil; // + std::vector accumulatedVolAlongTof_gas; // + }; + +private: + std::vector m_tofInIncreasingOrder; + std::vector m_contributingWells; +}; +