From 69accdc4c62dfe9091265363f6c4b2d98798e9b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Jensen?= Date: Thu, 7 Jun 2018 10:02:54 +0200 Subject: [PATCH] #2659 Ensemble statistics. First version --- .../RifEclipseSummaryAddress.cpp | 20 + .../FileInterface/RifEclipseSummaryAddress.h | 11 +- .../Summary/CMakeLists_files.cmake | 2 + .../Summary/RimEnsembleCurveSet.cpp | 353 +++++++++++++++++- .../Summary/RimEnsembleCurveSet.h | 72 +++- .../Summary/RimEnsembleStatistics.cpp | 105 ++++++ .../Summary/RimEnsembleStatistics.h | 64 ++++ .../Summary/RimSummaryCurve.cpp | 3 +- .../RigTimeHistoryCurveMerger.cpp | 8 + .../RigTimeHistoryCurveMerger.h | 1 + 10 files changed, 632 insertions(+), 7 deletions(-) create mode 100644 ApplicationCode/ProjectDataModel/Summary/RimEnsembleStatistics.cpp create mode 100644 ApplicationCode/ProjectDataModel/Summary/RimEnsembleStatistics.h diff --git a/ApplicationCode/FileInterface/RifEclipseSummaryAddress.cpp b/ApplicationCode/FileInterface/RifEclipseSummaryAddress.cpp index e1438d7f31..bb13b80165 100644 --- a/ApplicationCode/FileInterface/RifEclipseSummaryAddress.cpp +++ b/ApplicationCode/FileInterface/RifEclipseSummaryAddress.cpp @@ -25,6 +25,7 @@ #include "cvfAssert.h" + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -474,6 +475,17 @@ RifEclipseSummaryAddress RifEclipseSummaryAddress::importedAddress(const std::st return addr; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifEclipseSummaryAddress RifEclipseSummaryAddress::ensembleStatisticsAddress(const std::string& quantityName) +{ + RifEclipseSummaryAddress addr; + addr.m_variableCategory = SUMMARY_ENSEMBLE_STATISTICS; + addr.m_quantityName = quantityName; + return addr; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -775,6 +787,14 @@ bool operator==(const RifEclipseSummaryAddress& first, const RifEclipseSummaryAd return true; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool operator!=(const RifEclipseSummaryAddress& first, const RifEclipseSummaryAddress& second) +{ + return !operator==(first, second); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/FileInterface/RifEclipseSummaryAddress.h b/ApplicationCode/FileInterface/RifEclipseSummaryAddress.h index 6d730f925d..0562d37444 100644 --- a/ApplicationCode/FileInterface/RifEclipseSummaryAddress.h +++ b/ApplicationCode/FileInterface/RifEclipseSummaryAddress.h @@ -23,6 +23,12 @@ class QTextStream; + +#define ENSEMBLE_STAT_P10_QUANTITY_NAME "P10" +#define ENSEMBLE_STAT_P50_QUANTITY_NAME "P50" +#define ENSEMBLE_STAT_P90_QUANTITY_NAME "P90" +#define ENSEMBLE_STAT_MEAN_QUANTITY_NAME "MEAN" + //================================================================================================== // // @@ -50,7 +56,8 @@ public: SUMMARY_BLOCK, SUMMARY_BLOCK_LGR, SUMMARY_CALCULATED, - SUMMARY_IMPORTED + SUMMARY_IMPORTED, + SUMMARY_ENSEMBLE_STATISTICS }; enum SummaryIdentifierType @@ -134,6 +141,7 @@ public: static RifEclipseSummaryAddress blockLgrAddress(const std::string& quantityName, const std::string& lgrName, int i, int j, int k); static RifEclipseSummaryAddress calculatedAddress(const std::string& quantityName); static RifEclipseSummaryAddress importedAddress(const std::string& quantityName); + static RifEclipseSummaryAddress ensembleStatisticsAddress(const std::string& quantityName); // Access methods @@ -191,6 +199,7 @@ private: }; bool operator==(const RifEclipseSummaryAddress& first, const RifEclipseSummaryAddress& second); +bool operator!=(const RifEclipseSummaryAddress& first, const RifEclipseSummaryAddress& second); bool operator<(const RifEclipseSummaryAddress& first, const RifEclipseSummaryAddress& second); diff --git a/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake b/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake index d7501fb6b0..1475c01617 100644 --- a/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake +++ b/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake @@ -34,6 +34,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveSet.h ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveSetColorManager.h ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveFilter.h ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveFilterCollection.h +${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatistics.h ) set (SOURCE_GROUP_SOURCE_FILES @@ -71,6 +72,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveSet.cpp ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveSetColorManager.cpp ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveFilter.cpp ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveFilterCollection.cpp +${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatistics.cpp ) list(APPEND CODE_HEADER_FILES diff --git a/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp index 05ec5b873f..a173307ed7 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp @@ -25,10 +25,14 @@ #include "RifReaderEclipseSummary.h" +#include "RigStatisticsMath.h" +#include "RigTimeHistoryCurveMerger.h" + #include "RimEnsembleCurveFilter.h" #include "RimEnsembleCurveFilterCollection.h" #include "RimEnsembleCurveSetCollection.h" #include "RimEnsembleCurveSetColorManager.h" +#include "RimEnsembleStatistics.h" #include "RimProject.h" #include "RimRegularLegendConfig.h" #include "RimSummaryAddress.h" @@ -137,6 +141,10 @@ RimEnsembleCurveSet::RimEnsembleCurveSet() CAF_PDM_InitFieldNoDefault(&m_curveFilters, "CurveFilters", "Curve Filters", "", "", ""); m_curveFilters = new RimEnsembleCurveFilterCollection(); + CAF_PDM_InitFieldNoDefault(&m_statistics, "Statistics", "Statistics", "", "", ""); + m_statistics = new RimEnsembleStatistics(); + m_statistics.uiCapability()->setUiTreeHidden(true); + CAF_PDM_InitField(&m_userDefinedName, "UserDefinedName", QString("Ensemble Curve Set"), "Curve Set Name", "", "", ""); CAF_PDM_InitFieldNoDefault(&m_autoGeneratedName, "AutoGeneratedName", "Curve Set Name", "", "", ""); @@ -153,6 +161,9 @@ RimEnsembleCurveSet::RimEnsembleCurveSet() m_qwtPlotCurveForLegendText = new QwtPlotCurve; m_qwtPlotCurveForLegendText->setLegendAttribute(QwtPlotCurve::LegendShowSymbol, true); + + m_ensembleStatCase.reset(new RimEnsembleStatisticsCase(this)); + m_ensembleStatCase->createSummaryReaderInterface(); } //-------------------------------------------------------------------------------------------------- @@ -200,6 +211,7 @@ void RimEnsembleCurveSet::loadDataAndUpdate(bool updateParentPlot) m_yValuesUiFilterResultSelection = m_yValuesCurveVariable->address(); updateAllCurves(); + updateStatisticsCurves(true); if (updateParentPlot) { @@ -288,9 +300,45 @@ std::vector RimEnsembleCurveSet::curves() const //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimEnsembleCurveSet::deleteAllCurves() +void RimEnsembleCurveSet::deleteEnsembleCurves() { - m_curves.deleteAllChildObjects(); + std::vector curvesIndexesToDelete; + for (int c = 0; c < m_curves.size(); c++) + { + RimSummaryCurve* curve = m_curves[c]; + if (curve->summaryAddressY().category() != RifEclipseSummaryAddress::SUMMARY_ENSEMBLE_STATISTICS) + curvesIndexesToDelete.push_back(c); + } + + while (curvesIndexesToDelete.size() > 0) + { + int currIndex = curvesIndexesToDelete.back(); + delete m_curves[currIndex]; + m_curves.erase(currIndex); + curvesIndexesToDelete.pop_back(); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleCurveSet::deleteStatisticsCurves() +{ + std::vector curvesIndexesToDelete; + for (int c = 0; c < m_curves.size(); c++) + { + RimSummaryCurve* curve = m_curves[c]; + if (curve->summaryAddressY().category() == RifEclipseSummaryAddress::SUMMARY_ENSEMBLE_STATISTICS) + curvesIndexesToDelete.push_back(c); + } + + while (curvesIndexesToDelete.size() > 0) + { + int currIndex = curvesIndexesToDelete.back(); + delete m_curves[currIndex]; + m_curves.erase(currIndex); + curvesIndexesToDelete.pop_back(); + } } //-------------------------------------------------------------------------------------------------- @@ -380,6 +428,7 @@ void RimEnsembleCurveSet::fieldChangedByUi(const caf::PdmFieldHandle* changedFie m_yValuesCurveVariable->setAddress(m_yValuesUiFilterResultSelection()); updateAllCurves(); + updateStatisticsCurves(true); updateTextInPlot = true; } @@ -388,7 +437,8 @@ void RimEnsembleCurveSet::fieldChangedByUi(const caf::PdmFieldHandle* changedFie // Empty address cache m_allAddressesCache.clear(); updateAllCurves(); - + updateStatisticsCurves(true); + updateTextInPlot = true; } else if (changedField == &m_color) @@ -524,6 +574,9 @@ void RimEnsembleCurveSet::defineUiOrdering(QString uiConfigName, caf::PdmUiOrder } } + caf::PdmUiGroup* statGroup = uiOrdering.addNewGroup("Statistics"); + + m_statistics->defineUiOrdering(uiConfigName, *statGroup); uiOrdering.skipRemainingFields(true); } @@ -818,7 +871,7 @@ void RimEnsembleCurveSet::updateAllCurves() firstAncestorOrThisOfType(plot); CVF_ASSERT(plot); - deleteAllCurves(); + deleteEnsembleCurves(); m_qwtPlotCurveForLegendText->detach(); RimSummaryCaseCollection* group = m_yValuesSummaryGroup(); @@ -862,6 +915,57 @@ void RimEnsembleCurveSet::updateAllCurves() updateCurveColors(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleCurveSet::updateStatisticsCurves(bool calculate = true) +{ + using SAddr = RifEclipseSummaryAddress; + + if (calculate) + { + // Calculate + m_ensembleStatCase->calculate(m_yValuesSummaryGroup, m_yValuesCurveVariable->address()); + } + + RimSummaryPlot* plot = nullptr; + firstAncestorOrThisOfType(plot); + CVF_ASSERT(plot); + + std::vector addresses; + if (m_statistics->isActive()) + { + if (m_statistics->showP10Curve()) addresses.push_back(SAddr::ensembleStatisticsAddress(ENSEMBLE_STAT_P10_QUANTITY_NAME)); + if (m_statistics->showP50Curve()) addresses.push_back(SAddr::ensembleStatisticsAddress(ENSEMBLE_STAT_P50_QUANTITY_NAME)); + if (m_statistics->showP90Curve()) addresses.push_back(SAddr::ensembleStatisticsAddress(ENSEMBLE_STAT_P90_QUANTITY_NAME)); + if (m_statistics->showMeanCurve()) addresses.push_back(SAddr::ensembleStatisticsAddress(ENSEMBLE_STAT_MEAN_QUANTITY_NAME)); + } + + deleteStatisticsCurves(); + for (auto address : addresses) + { + auto curve = new RimSummaryCurve(); + curve->setParentQwtPlotNoReplot(plot->qwtPlot()); + m_curves.push_back(curve); + curve->setSymbol(RimPlotCurve::SYMBOL_ELLIPSE); + curve->setSymbolSkipDinstance(50); + curve->setLineStyle(RimPlotCurve::STYLE_SOLID); + curve->setSummaryCaseY(m_ensembleStatCase.get()); + curve->setSummaryAddressY(address); + curve->setZOrder(1000); + + curve->updateCurveVisibility(false); + curve->loadDataAndUpdate(false); + } + + if (plot->qwtPlot()) + { + plot->qwtPlot()->updateLegend(); + plot->qwtPlot()->replot(); + plot->updateAxes(); + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -1035,3 +1139,244 @@ void RimEnsembleCurveSet::updateLegendMappingMode() break; } } + + + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifEnsembleStatisticsReader::RifEnsembleStatisticsReader(RimEnsembleStatisticsCase* ensStatCase) +{ + CVF_ASSERT(ensStatCase); + + m_ensembleStatCase = ensStatCase; + + m_allResultAddresses = std::vector( + { + RifEclipseSummaryAddress::ensembleStatisticsAddress(ENSEMBLE_STAT_P10_QUANTITY_NAME), + RifEclipseSummaryAddress::ensembleStatisticsAddress(ENSEMBLE_STAT_P50_QUANTITY_NAME), + RifEclipseSummaryAddress::ensembleStatisticsAddress(ENSEMBLE_STAT_P90_QUANTITY_NAME), + RifEclipseSummaryAddress::ensembleStatisticsAddress(ENSEMBLE_STAT_MEAN_QUANTITY_NAME) + }); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const std::vector& RifEnsembleStatisticsReader::timeSteps(const RifEclipseSummaryAddress& resultAddress) const +{ + return m_ensembleStatCase->timeSteps(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifEnsembleStatisticsReader::values(const RifEclipseSummaryAddress& resultAddress, std::vector* values) const +{ + if (!validateAddress(resultAddress)) return false; + + const std::vector* sourceData = nullptr; + if (resultAddress.quantityName() == ENSEMBLE_STAT_P10_QUANTITY_NAME) sourceData = &m_ensembleStatCase->p10(); + else if (resultAddress.quantityName() == ENSEMBLE_STAT_P50_QUANTITY_NAME) sourceData = &m_ensembleStatCase->p50(); + else if (resultAddress.quantityName() == ENSEMBLE_STAT_P90_QUANTITY_NAME) sourceData = &m_ensembleStatCase->p90(); + else if (resultAddress.quantityName() == ENSEMBLE_STAT_MEAN_QUANTITY_NAME) sourceData = &m_ensembleStatCase->mean(); + + if (!sourceData) return false; + + values->clear(); + values->reserve(sourceData->size()); + for (auto val : *sourceData) values->push_back(val); + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::string RifEnsembleStatisticsReader::unitName(const RifEclipseSummaryAddress& resultAddress) const +{ + return "(RifEnsembleStatisticsReader::unitName)"; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifEnsembleStatisticsReader::validateAddress(const RifEclipseSummaryAddress& address) const +{ + return address.category() == RifEclipseSummaryAddress::SUMMARY_ENSEMBLE_STATISTICS && + !address.quantityName().empty(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimEnsembleStatisticsCase::RimEnsembleStatisticsCase(RimEnsembleCurveSet* curveSet) +{ + m_curveSet = curveSet; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const std::vector& RimEnsembleStatisticsCase::timeSteps() const +{ + return m_timeSteps; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const std::vector& RimEnsembleStatisticsCase::p10() const +{ + return m_p10Data; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const std::vector& RimEnsembleStatisticsCase::p50() const +{ + return m_p50Data; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const std::vector& RimEnsembleStatisticsCase::p90() const +{ + return m_p90Data; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const std::vector& RimEnsembleStatisticsCase::mean() const +{ + return m_meanData; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimEnsembleStatisticsCase::caseName() +{ + return "Ensemble Statistics"; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleStatisticsCase::createSummaryReaderInterface() +{ + m_statisticsReader.reset(new RifEnsembleStatisticsReader(this)); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifSummaryReaderInterface* RimEnsembleStatisticsCase::summaryReader() +{ + return m_statisticsReader.get(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const RimEnsembleCurveSet* RimEnsembleStatisticsCase::curveSet() const +{ + return m_curveSet; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleStatisticsCase::calculate(const RimSummaryCaseCollection* ensemble, const RifEclipseSummaryAddress& inputAddress) +{ + RigTimeHistoryCurveMerger curveMerger; + int caseCount = (int)ensemble->allSummaryCases().size(); + + { + for (const auto& sumCase : ensemble->allSummaryCases()) + { + const auto& reader = sumCase->summaryReader(); + if (reader) + { + std::vector timeSteps = reader->timeSteps(inputAddress); + std::vector values; + reader->values(inputAddress, &values); + + curveMerger.addCurveData(values, timeSteps); + } + } + curveMerger.computeInterpolatedValues(); + } + + const std::vector& allTimeSteps = curveMerger.allTimeSteps(); + std::vector> allValues; + { + for (int c = 0; c < caseCount; c++) + { + allValues.push_back(curveMerger.interpolatedCurveValuesForAllTimeSteps(c)); + } + } + + clearData(); + m_timeSteps = allTimeSteps; + + for (int t = 0; t < (int)allTimeSteps.size(); t++) + { + std::vector valuesForTimeSteps; + valuesForTimeSteps.reserve(caseCount); + + for (int c = 0; c < caseCount; c++) + { + valuesForTimeSteps.push_back(allValues[c][t]); + } + + { + double min, max, range, mean, stdev; + RigStatisticsMath::calculateBasicStatistics(valuesForTimeSteps, &min, &max, nullptr, &range, &mean, &stdev); + + std::vector histogram; + RigHistogramCalculator histCalc(min, max, 100, &histogram); + histCalc.addData(valuesForTimeSteps); + + double p10, p50, p90; + p10 = histCalc.calculatePercentil(0.1); + p50 = histCalc.calculatePercentil(0.5); + p90 = histCalc.calculatePercentil(0.9); + + m_p10Data.push_back(p10); + m_p50Data.push_back(p50); + m_p90Data.push_back(p90); + m_meanData.push_back(mean); + } + + } + m_addressUsedInLastCalculation = inputAddress; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleStatisticsCase::calculate() +{ + auto inputAddress = m_curveSet->summaryAddress(); + auto ensemble = m_curveSet->summaryCaseCollection(); + if (m_statisticsReader && ensemble && inputAddress.isValid()) + { + calculate(ensemble, inputAddress); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleStatisticsCase::clearData() +{ + m_timeSteps.clear(); + m_p10Data.clear(); + m_p50Data.clear(); + m_p90Data.clear(); + m_meanData.clear(); + m_addressUsedInLastCalculation = RifEclipseSummaryAddress(); +} diff --git a/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h index 5ebb705ea0..8475e231d1 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h +++ b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h @@ -20,10 +20,12 @@ #pragma once #include "RifEclipseSummaryAddress.h" +#include "RifSummaryReaderInterface.h" #include "RiaDefines.h" #include "RimRegularLegendConfig.h" +#include "RimSummaryCase.h" #include "RimSummaryCaseCollection.h" #include "cafPdmFieldCvfColor.h" @@ -48,7 +50,70 @@ class RimSummaryFilter; class RimSummaryPlotSourceStepping; class RimSummaryCurveAutoName; class RimEnsembleCurveFilterCollection; +class RimEnsembleStatistics; class QKeyEvent; +class RimEnsembleStatisticsCase; + + +//================================================================================================== +/// +//================================================================================================== +class RifEnsembleStatisticsReader : public RifSummaryReaderInterface +{ +public: + RifEnsembleStatisticsReader(RimEnsembleStatisticsCase* ensStatCase); + + virtual const std::vector& timeSteps(const RifEclipseSummaryAddress& resultAddress) const override; + virtual bool values(const RifEclipseSummaryAddress& resultAddress, std::vector* values) const override; + virtual std::string unitName(const RifEclipseSummaryAddress& resultAddress) const override; + +private: + bool validateAddress(const RifEclipseSummaryAddress& address) const; + +private: + RimEnsembleStatisticsCase * m_ensembleStatCase; +}; + +//================================================================================================== +/// +//================================================================================================== +class RimEnsembleStatisticsCase : public RimSummaryCase +{ +public: + RimEnsembleStatisticsCase(RimEnsembleCurveSet* curveSet); + + const std::vector& timeSteps() const; + const std::vector& p10() const; + const std::vector& p50() const; + const std::vector& p90() const; + const std::vector& mean() const; + + virtual QString caseName() override; + virtual void createSummaryReaderInterface() override; + virtual RifSummaryReaderInterface* summaryReader() override; + + virtual void updateFilePathsFromProjectPath(const QString& newProjectPath, const QString& oldProjectPath) override {} + + const RimEnsembleCurveSet* curveSet() const; + + void calculate(); + void calculate(const RimSummaryCaseCollection* ensemble, const RifEclipseSummaryAddress& inputAddress); + +private: + void clearData(); + +private: + std::unique_ptr m_statisticsReader; + RimEnsembleCurveSet* m_curveSet; + + std::vector m_timeSteps; + std::vector m_p10Data; + std::vector m_p50Data; + std::vector m_p90Data; + std::vector m_meanData; + + RifEclipseSummaryAddress m_addressUsedInLastCalculation; +}; //================================================================================================== @@ -78,7 +143,8 @@ public: RifEclipseSummaryAddress summaryAddress() const; std::vector curves() const; - void deleteAllCurves(); + void deleteEnsembleCurves(); + void deleteStatisticsCurves(); RimRegularLegendConfig* legendConfig(); void onLegendDefinitionChanged(); @@ -91,6 +157,7 @@ public: EnsembleParameter::Type currentEnsembleParameterType() const; void updateAllCurves(); + void updateStatisticsCurves(bool calculate); RimEnsembleCurveSet* clone() const; void showCurves(bool show); @@ -148,6 +215,7 @@ private: caf::PdmChildField m_legendConfig; caf::PdmChildField m_curveFilters; + caf::PdmChildField m_statistics; caf::PdmField m_isUsingAutoName; caf::PdmField m_userDefinedName; @@ -157,5 +225,7 @@ private: std::set m_allAddressesCache; QwtPlotCurve* m_qwtPlotCurveForLegendText; + + std::unique_ptr m_ensembleStatCase; }; diff --git a/ApplicationCode/ProjectDataModel/Summary/RimEnsembleStatistics.cpp b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleStatistics.cpp new file mode 100644 index 0000000000..94e6d1b6db --- /dev/null +++ b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleStatistics.cpp @@ -0,0 +1,105 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017- Statoil 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 "RimEnsembleStatistics.h" + +#include "RifSummaryReaderInterface.h" + +#include "RigStatisticsMath.h" +#include "RigTimeHistoryCurveMerger.h" + +#include "RimEnsembleCurveSet.h" +#include "RimSummaryCase.h" +#include "RimSummaryCaseCollection.h" + + +CAF_PDM_SOURCE_INIT(RimEnsembleStatistics, "RimEnsembleStatistics"); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimEnsembleStatistics::RimEnsembleStatistics() +{ + CAF_PDM_InitObject("Ensemble Curve Filter", ":/EnsembleCurveSet16x16.png", "", ""); + + CAF_PDM_InitFieldNoDefault(&m_active, "Active", "Active", "", "", ""); + CAF_PDM_InitFieldNoDefault(&m_showP10Curve, "ShowP10Curve", "P10", "", "", ""); + CAF_PDM_InitFieldNoDefault(&m_showP50Curve, "ShowP50Curve", "P50", "", "", ""); + CAF_PDM_InitFieldNoDefault(&m_showP90Curve, "ShowP90Curve", "P90", "", "", ""); + CAF_PDM_InitFieldNoDefault(&m_showMeanCurve, "ShowPMeanCurve", "Mean", "", "", ""); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimEnsembleStatistics::isActive() const +{ + return m_active; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QList RimEnsembleStatistics::calculateValueOptions(const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly) +{ + QList options; + + + return options; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleStatistics::fieldChangedByUi(const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue) +{ + if (changedField == &m_active || + changedField == &m_showP10Curve || + changedField == &m_showP50Curve || + changedField == &m_showP90Curve || + changedField == &m_showMeanCurve) + { + if (!parentCurveSet()) return; + + parentCurveSet()->updateStatisticsCurves(false); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEnsembleStatistics::defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) +{ + uiOrdering.add(&m_active); + uiOrdering.add(&m_showP10Curve); + uiOrdering.add(&m_showP50Curve); + uiOrdering.add(&m_showP90Curve); + uiOrdering.add(&m_showMeanCurve); + + uiOrdering.skipRemainingFields(true); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimEnsembleCurveSet* RimEnsembleStatistics::parentCurveSet() const +{ + RimEnsembleCurveSet* curveSet; + firstAncestorOrThisOfType(curveSet); + return curveSet; +} diff --git a/ApplicationCode/ProjectDataModel/Summary/RimEnsembleStatistics.h b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleStatistics.h new file mode 100644 index 0000000000..5315d607a2 --- /dev/null +++ b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleStatistics.h @@ -0,0 +1,64 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017- Statoil 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 "RimEnsembleCurveSet.h" +#include "RimSummaryCase.h" + +#include "cafPdmField.h" +#include "cafPdmObject.h" + +class RifEclipseSummaryAddress; +class RimSummaryCaseCollection; +class RimEnsembleStatisticsCase; + + +//================================================================================================== +/// +//================================================================================================== +class RimEnsembleStatistics : public caf::PdmObject +{ + CAF_PDM_HEADER_INIT; + +public: + RimEnsembleStatistics(); + + bool isActive() const; + bool showP10Curve() const { return m_showP10Curve; }; + bool showP50Curve() const { return m_showP50Curve; }; + bool showP90Curve() const { return m_showP90Curve; }; + bool showMeanCurve() const { return m_showMeanCurve; }; + + virtual QList calculateValueOptions(const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly) override; + virtual void fieldChangedByUi(const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue) override; + virtual void defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) override; + + RimEnsembleCurveSet* parentCurveSet() const; + +private: + caf::PdmField m_active; + caf::PdmField m_showP10Curve; + caf::PdmField m_showP50Curve; + caf::PdmField m_showP90Curve; + caf::PdmField m_showMeanCurve; + + RimSummaryCaseCollection* m_ensemble; +}; + diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.cpp b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.cpp index e11bf740e3..798b1e60db 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.cpp @@ -889,8 +889,9 @@ void RimSummaryCurve::calculateCurveInterpolationFromAddress() { if (m_yValuesCurveVariable()) { + auto category = m_yValuesCurveVariable()->address().category(); QString quantityName = QString::fromUtf8(m_yValuesCurveVariable()->address().quantityName().c_str()); - if (quantityName.endsWith("T")) + if (quantityName.endsWith("T") || category == RifEclipseSummaryAddress::SUMMARY_ENSEMBLE_STATISTICS) { m_curveInterpolation = INTERPOLATION_POINT_TO_POINT; } diff --git a/ApplicationCode/ReservoirDataModel/RigTimeHistoryCurveMerger.cpp b/ApplicationCode/ReservoirDataModel/RigTimeHistoryCurveMerger.cpp index 19d7b4634d..ec9f45719e 100644 --- a/ApplicationCode/ReservoirDataModel/RigTimeHistoryCurveMerger.cpp +++ b/ApplicationCode/ReservoirDataModel/RigTimeHistoryCurveMerger.cpp @@ -76,6 +76,14 @@ std::vector& RigTimeHistoryCurveMerger::interpolatedCurveValuesForAllTim return m_interpolatedValuesForAllCurves[curveIdx]; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +int RigTimeHistoryCurveMerger::interploatedCurveCount() const +{ + return static_cast(m_interpolatedValuesForAllCurves.size()); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/ReservoirDataModel/RigTimeHistoryCurveMerger.h b/ApplicationCode/ReservoirDataModel/RigTimeHistoryCurveMerger.h index 5a1de82f9d..325c163a6d 100644 --- a/ApplicationCode/ReservoirDataModel/RigTimeHistoryCurveMerger.h +++ b/ApplicationCode/ReservoirDataModel/RigTimeHistoryCurveMerger.h @@ -40,6 +40,7 @@ public: RigCurveDataTools::CurveIntervals validIntervalsForAllTimeSteps() const; const std::vector& allTimeSteps() const; const std::vector& interpolatedCurveValuesForAllTimeSteps(size_t curveIdx) const; + int interploatedCurveCount() const; // Non-const access is not required by any clients, but the expression parser has no available const interface // for specifying a data source for an expression variable. Allow non-const access to avoid copy of the contained