From 898df5b68aced5d0e61520d5d6e7aa9bbcc91538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Jensen?= Date: Mon, 25 Jun 2018 15:14:47 +0200 Subject: [PATCH] #3096 Implemented ensemble calulations --- .../Application/RiaApplication.cpp | 12 + .../Tools/RiaSummaryCurveAnalyzer.cpp | 2 +- .../Tools/RiaSummaryCurveAnalyzer.h | 2 +- .../RicCreateSummaryCaseCollectionFeature.cpp | 7 +- .../RicCreateSummaryCaseCollectionFeature.h | 3 +- .../Commands/RicImportEnsembleFeature.cpp | 7 +- .../CMakeLists_files.cmake | 2 + .../RicNewDerivedEnsembleFeature.cpp | 86 ++++ .../RicNewDerivedEnsembleFeature.h | 39 ++ .../RicSummaryCurveCreator.cpp | 9 +- .../FileInterface/CMakeLists_files.cmake | 2 + .../RifCaseRealizationParametersReader.cpp | 3 +- .../RifCaseRealizationParametersReader.h | 1 + .../FileInterface/RifColumnBasedUserData.cpp | 2 +- .../FileInterface/RifCsvUserData.cpp | 2 +- .../RifDerivedEnsembleReader.cpp | 73 +++ .../FileInterface/RifDerivedEnsembleReader.h | 42 ++ .../FileInterface/RifEclipseSummaryTools.cpp | 2 +- .../RifEnsembleStatisticsReader.cpp | 6 +- .../RifKeywordVectorUserData.cpp | 2 +- .../FileInterface/RifReaderEclipseSummary.cpp | 2 +- .../FileInterface/RifReaderObservedData.cpp | 8 +- .../RifSummaryReaderInterface.cpp | 2 +- .../FileInterface/RifSummaryReaderInterface.h | 5 +- .../RimContextCommandBuilder.cpp | 3 + .../ProjectDataModel/RimProject.cpp | 9 + ApplicationCode/ProjectDataModel/RimProject.h | 4 +- .../Summary/CMakeLists_files.cmake | 4 + .../RimCalculatedSummaryCurveReader.cpp | 2 +- .../Summary/RimDerivedEnsembleCase.cpp | 220 +++++++++ .../Summary/RimDerivedEnsembleCase.h | 78 ++++ .../RimDerivedEnsembleCaseCollection.cpp | 440 ++++++++++++++++++ .../RimDerivedEnsembleCaseCollection.h | 85 ++++ .../Summary/RimEnsembleCurveSet.cpp | 3 +- .../Summary/RimSummaryCaseCollection.cpp | 65 ++- .../Summary/RimSummaryCaseCollection.h | 19 +- .../Summary/RimSummaryCaseMainCollection.cpp | 39 +- .../Summary/RimSummaryCaseMainCollection.h | 7 +- .../Summary/RimSummaryCurve.cpp | 2 +- .../RigCaseRealizationParameters.cpp | 47 ++ .../RigCaseRealizationParameters.h | 6 + .../RiuSummaryCurveDefSelection.cpp | 106 +++-- .../RiuSummaryCurveDefSelection.h | 2 +- doc/summary_cases_and_readers.plantuml | 15 +- 44 files changed, 1378 insertions(+), 99 deletions(-) create mode 100644 ApplicationCode/Commands/SummaryPlotCommands/RicNewDerivedEnsembleFeature.cpp create mode 100644 ApplicationCode/Commands/SummaryPlotCommands/RicNewDerivedEnsembleFeature.h create mode 100644 ApplicationCode/FileInterface/RifDerivedEnsembleReader.cpp create mode 100644 ApplicationCode/FileInterface/RifDerivedEnsembleReader.h create mode 100644 ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCase.cpp create mode 100644 ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCase.h create mode 100644 ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCaseCollection.cpp create mode 100644 ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCaseCollection.h diff --git a/ApplicationCode/Application/RiaApplication.cpp b/ApplicationCode/Application/RiaApplication.cpp index bbf4946c09..402692e6ee 100644 --- a/ApplicationCode/Application/RiaApplication.cpp +++ b/ApplicationCode/Application/RiaApplication.cpp @@ -57,6 +57,7 @@ #include "RimRftPlotCollection.h" #include "RimStimPlanColors.h" #include "RimSummaryCase.h" +#include "RimSummaryCaseCollection.h" #include "RimSummaryCaseMainCollection.h" #include "RimSummaryCrossPlotCollection.h" #include "RimSummaryPlot.h" @@ -597,6 +598,17 @@ bool RiaApplication::loadProject(const QString& projectFileName, ProjectLoadActi cas->intersectionViewCollection()->syncFromExistingIntersections(false); } + // Init summary case groups + for (RimOilField* oilField : m_project->oilFields) + { + auto sumMainCollection = oilField->summaryCaseMainCollection(); + if(!sumMainCollection) continue; + + for (auto sumCaseGroup : sumMainCollection->summaryCaseCollections()) + { + sumCaseGroup->loadDataAndUpdate(); + } + } loadAndUpdatePlotData(); diff --git a/ApplicationCode/Application/Tools/RiaSummaryCurveAnalyzer.cpp b/ApplicationCode/Application/Tools/RiaSummaryCurveAnalyzer.cpp index 595df584ba..b23b143140 100644 --- a/ApplicationCode/Application/Tools/RiaSummaryCurveAnalyzer.cpp +++ b/ApplicationCode/Application/Tools/RiaSummaryCurveAnalyzer.cpp @@ -130,7 +130,7 @@ std::vector RiaSummaryCurveAnalyzer::identifierTexts(RifEclipseSummaryA /// //-------------------------------------------------------------------------------------------------- std::vector - RiaSummaryCurveAnalyzer::addressesForCategory(const std::vector& addresses, + RiaSummaryCurveAnalyzer::addressesForCategory(const std::set& addresses, RifEclipseSummaryAddress::SummaryVarCategory category) { std::vector filteredAddresses; diff --git a/ApplicationCode/Application/Tools/RiaSummaryCurveAnalyzer.h b/ApplicationCode/Application/Tools/RiaSummaryCurveAnalyzer.h index ba12c9cef8..e66475241f 100644 --- a/ApplicationCode/Application/Tools/RiaSummaryCurveAnalyzer.h +++ b/ApplicationCode/Application/Tools/RiaSummaryCurveAnalyzer.h @@ -50,7 +50,7 @@ public: std::vector identifierTexts(RifEclipseSummaryAddress::SummaryVarCategory category) const; - static std::vector addressesForCategory(const std::vector& addresses, + static std::vector addressesForCategory(const std::set& addresses, RifEclipseSummaryAddress::SummaryVarCategory category); private: diff --git a/ApplicationCode/Commands/RicCreateSummaryCaseCollectionFeature.cpp b/ApplicationCode/Commands/RicCreateSummaryCaseCollectionFeature.cpp index 7e0ac8a2e0..5100018ba9 100644 --- a/ApplicationCode/Commands/RicCreateSummaryCaseCollectionFeature.cpp +++ b/ApplicationCode/Commands/RicCreateSummaryCaseCollectionFeature.cpp @@ -36,19 +36,20 @@ CAF_CMD_SOURCE_INIT(RicCreateSummaryCaseCollectionFeature, "RicCreateSummaryCase //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RicCreateSummaryCaseCollectionFeature::groupSummaryCases(std::vector cases, const QString& groupName, bool isEnsemble) +RimSummaryCaseCollection* RicCreateSummaryCaseCollectionFeature::groupSummaryCases(std::vector cases, const QString& groupName, bool isEnsemble) { RimSummaryCaseMainCollection* summaryCaseMainCollection = nullptr; if (!cases.empty()) { cases[0]->firstAncestorOrThisOfTypeAsserted(summaryCaseMainCollection); - summaryCaseMainCollection->addCaseCollection(cases, groupName, isEnsemble); + auto newGroup = summaryCaseMainCollection->addCaseCollection(cases, groupName, isEnsemble); summaryCaseMainCollection->updateConnectedEditors(); RiuPlotMainWindowTools::showPlotMainWindow(); - RiuPlotMainWindowTools::selectAsCurrentItem(summaryCaseMainCollection->summaryCaseCollections().back()->allSummaryCases().front()); + return newGroup; } + return nullptr; } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/Commands/RicCreateSummaryCaseCollectionFeature.h b/ApplicationCode/Commands/RicCreateSummaryCaseCollectionFeature.h index eb8a1fbb78..ef97f8ca6d 100644 --- a/ApplicationCode/Commands/RicCreateSummaryCaseCollectionFeature.h +++ b/ApplicationCode/Commands/RicCreateSummaryCaseCollectionFeature.h @@ -23,6 +23,7 @@ #include class RimSummaryCase; +class RimSummaryCaseCollection; //================================================================================================== /// @@ -31,7 +32,7 @@ class RicCreateSummaryCaseCollectionFeature : public caf::CmdFeature { CAF_CMD_HEADER_INIT; - static void groupSummaryCases(std::vector cases, const QString& groupName, bool isEnsemble = false); + static RimSummaryCaseCollection* groupSummaryCases(std::vector cases, const QString& groupName, bool isEnsemble = false); private: virtual bool isCommandEnabled() override; diff --git a/ApplicationCode/Commands/RicImportEnsembleFeature.cpp b/ApplicationCode/Commands/RicImportEnsembleFeature.cpp index 54fb821e9d..fab0a38770 100644 --- a/ApplicationCode/Commands/RicImportEnsembleFeature.cpp +++ b/ApplicationCode/Commands/RicImportEnsembleFeature.cpp @@ -130,13 +130,12 @@ void RicImportEnsembleFeature::onActionTriggered(bool isChecked) validateEnsembleCases(cases); RicImportSummaryCasesFeature::addSummaryCases(cases); - RicCreateSummaryCaseCollectionFeature::groupSummaryCases(cases, ensembleName, true); + auto newGroup = RicCreateSummaryCaseCollectionFeature::groupSummaryCases(cases, ensembleName, true); RiuPlotMainWindow* mainPlotWindow = app->getOrCreateAndShowMainPlotWindow(); - if (mainPlotWindow && !cases.empty()) + if (mainPlotWindow && newGroup) { - mainPlotWindow->selectAsCurrentItem(cases.back()); - + mainPlotWindow->selectAsCurrentItem(newGroup); mainPlotWindow->updateSummaryPlotToolBar(); } diff --git a/ApplicationCode/Commands/SummaryPlotCommands/CMakeLists_files.cmake b/ApplicationCode/Commands/SummaryPlotCommands/CMakeLists_files.cmake index 7e9a2cb481..3a6d690770 100644 --- a/ApplicationCode/Commands/SummaryPlotCommands/CMakeLists_files.cmake +++ b/ApplicationCode/Commands/SummaryPlotCommands/CMakeLists_files.cmake @@ -35,6 +35,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RicNewSummaryCrossPlotCurveFeature.h ${CMAKE_CURRENT_LIST_DIR}/RicNewSummaryEnsembleCurveSetFeature.h ${CMAKE_CURRENT_LIST_DIR}/RicPasteEnsembleCurveSetFeature.h ${CMAKE_CURRENT_LIST_DIR}/RicNewEnsembleCurveFilterFeature.h +${CMAKE_CURRENT_LIST_DIR}/RicNewDerivedEnsembleFeature.h ) set (SOURCE_GROUP_SOURCE_FILES @@ -73,6 +74,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RicNewSummaryCrossPlotCurveFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicNewSummaryEnsembleCurveSetFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicPasteEnsembleCurveSetFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicNewEnsembleCurveFilterFeature.cpp +${CMAKE_CURRENT_LIST_DIR}/RicNewDerivedEnsembleFeature.cpp ) list(APPEND CODE_HEADER_FILES diff --git a/ApplicationCode/Commands/SummaryPlotCommands/RicNewDerivedEnsembleFeature.cpp b/ApplicationCode/Commands/SummaryPlotCommands/RicNewDerivedEnsembleFeature.cpp new file mode 100644 index 0000000000..c1ad462415 --- /dev/null +++ b/ApplicationCode/Commands/SummaryPlotCommands/RicNewDerivedEnsembleFeature.cpp @@ -0,0 +1,86 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2016- 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 "RicNewDerivedEnsembleFeature.h" + +#include "RiaApplication.h" + +#include "RimDerivedEnsembleCaseCollection.h" +#include "RimProject.h" +#include "RimSummaryCaseMainCollection.h" + +#include "RiuPlotMainWindowTools.h" + +#include "cafSelectionManagerTools.h" + +#include + +#include + + +CAF_CMD_SOURCE_INIT(RicNewDerivedEnsembleFeature, "RicNewDerivedEnsembleFeature"); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RicNewDerivedEnsembleFeature::isCommandEnabled() +{ + std::vector mainColls = caf::selectedObjectsByTypeStrict(); + std::vector ensembles = caf::selectedObjectsByTypeStrict(); + + return mainColls.size() == 1 || ensembles.size() == 1 || ensembles.size() == 2; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicNewDerivedEnsembleFeature::onActionTriggered(bool isChecked) +{ + if (isCommandEnabled()) + { + auto project = RiaApplication::instance()->project(); + auto mainColl = project->firstSummaryCaseMainCollection(); + + auto newColl = mainColl->addCaseCollection({}, "", true, []() {return new RimDerivedEnsembleCaseCollection(); }); + auto newEnsemble = dynamic_cast(newColl); + + { + std::vector ensembles = caf::selectedObjectsByType(); + + if (ensembles.size() >= 1) newEnsemble->setEnsemble1(ensembles[0]); + if (ensembles.size() == 2) + { + newEnsemble->setEnsemble2(ensembles[1]); + newEnsemble->updateDerivedEnsembleCases(); + } + } + + mainColl->updateConnectedEditors(); + RiuPlotMainWindowTools::selectAsCurrentItem(newEnsemble); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicNewDerivedEnsembleFeature::setupActionLook(QAction* actionToSetup) +{ + actionToSetup->setText("New Derived Ensemble"); + actionToSetup->setIcon(QIcon(":/SummaryEnsemble16x16.png")); +} + diff --git a/ApplicationCode/Commands/SummaryPlotCommands/RicNewDerivedEnsembleFeature.h b/ApplicationCode/Commands/SummaryPlotCommands/RicNewDerivedEnsembleFeature.h new file mode 100644 index 0000000000..2633e24cef --- /dev/null +++ b/ApplicationCode/Commands/SummaryPlotCommands/RicNewDerivedEnsembleFeature.h @@ -0,0 +1,39 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2016- 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 "cafCmdFeature.h" + +class RimSummaryPlotCollection; +class RimSummaryCase; +class RimSummaryPlot; + +//================================================================================================== +/// +//================================================================================================== +class RicNewDerivedEnsembleFeature : public caf::CmdFeature +{ + CAF_CMD_HEADER_INIT; + +protected: + // Overrides + virtual bool isCommandEnabled(); + virtual void onActionTriggered( bool isChecked ); + virtual void setupActionLook(QAction* actionToSetup); +}; diff --git a/ApplicationCode/Commands/SummaryPlotCommands/RicSummaryCurveCreator.cpp b/ApplicationCode/Commands/SummaryPlotCommands/RicSummaryCurveCreator.cpp index 79bc54bad2..65cd546118 100644 --- a/ApplicationCode/Commands/SummaryPlotCommands/RicSummaryCurveCreator.cpp +++ b/ApplicationCode/Commands/SummaryPlotCommands/RicSummaryCurveCreator.cpp @@ -27,6 +27,7 @@ #include "RifReaderEclipseSummary.h" +#include "RimDerivedEnsembleCaseCollection.h" #include "RimEnsembleCurveSet.h" #include "RimEnsembleCurveSetCollection.h" #include "RimEnsembleCurveSetColorManager.h" @@ -509,13 +510,13 @@ std::set RicSummaryCurveCreator::getAllSummaryWellNames() if (reader) { - const std::vector allAddresses = reader->allResultAddresses(); + const std::set allAddresses = reader->allResultAddresses(); - for (size_t i = 0; i < allAddresses.size(); i++) + for(auto& address : allAddresses) { - if (allAddresses[i].category() == RifEclipseSummaryAddress::SUMMARY_WELL) + if (address.category() == RifEclipseSummaryAddress::SUMMARY_WELL) { - summaryWellNames.insert(allAddresses[i].wellName()); + summaryWellNames.insert(address.wellName()); } } } diff --git a/ApplicationCode/FileInterface/CMakeLists_files.cmake b/ApplicationCode/FileInterface/CMakeLists_files.cmake index ff4d70570d..bb1261d65c 100644 --- a/ApplicationCode/FileInterface/CMakeLists_files.cmake +++ b/ApplicationCode/FileInterface/CMakeLists_files.cmake @@ -43,6 +43,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RifEnsembleParametersReader.h ${CMAKE_CURRENT_LIST_DIR}/RifCaseRealizationParametersReader.h ${CMAKE_CURRENT_LIST_DIR}/RifFileParseTools.h ${CMAKE_CURRENT_LIST_DIR}/RifEnsembleStatisticsReader.h +${CMAKE_CURRENT_LIST_DIR}/RifDerivedEnsembleReader.h # HDF5 file reader is directly included in ResInsight main CmakeList.txt #${CMAKE_CURRENT_LIST_DIR}/RifHdf5Reader.h @@ -90,6 +91,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RifEnsembleParametersReader.cpp ${CMAKE_CURRENT_LIST_DIR}/RifCaseRealizationParametersReader.cpp ${CMAKE_CURRENT_LIST_DIR}/RifFileParseTools.cpp ${CMAKE_CURRENT_LIST_DIR}/RifEnsembleStatisticsReader.cpp +${CMAKE_CURRENT_LIST_DIR}/RifDerivedEnsembleReader.cpp # HDF5 file reader is directly included in ResInsight main CmakeList.txt #${CMAKE_CURRENT_LIST_DIR}/RifHdf5Reader.cpp diff --git a/ApplicationCode/FileInterface/RifCaseRealizationParametersReader.cpp b/ApplicationCode/FileInterface/RifCaseRealizationParametersReader.cpp index 8ef6f884a2..97f3cd69b0 100644 --- a/ApplicationCode/FileInterface/RifCaseRealizationParametersReader.cpp +++ b/ApplicationCode/FileInterface/RifCaseRealizationParametersReader.cpp @@ -26,6 +26,7 @@ #include #include +#include //-------------------------------------------------------------------------------------------------- /// Constants @@ -166,8 +167,6 @@ void RifCaseRealizationParametersReader::openFile() if (!m_file->open(QIODevice::ReadOnly | QIODevice::Text)) { closeFile(); - //delete m_file; - //m_file = nullptr; throw FileParseException(QString("Failed to open %1").arg(m_fileName)); } } diff --git a/ApplicationCode/FileInterface/RifCaseRealizationParametersReader.h b/ApplicationCode/FileInterface/RifCaseRealizationParametersReader.h index 5b2c65da72..549a0d9943 100644 --- a/ApplicationCode/FileInterface/RifCaseRealizationParametersReader.h +++ b/ApplicationCode/FileInterface/RifCaseRealizationParametersReader.h @@ -56,6 +56,7 @@ private: void closeDataStream(); void openFile(); void closeFile(); + private: std::shared_ptr m_parameters; diff --git a/ApplicationCode/FileInterface/RifColumnBasedUserData.cpp b/ApplicationCode/FileInterface/RifColumnBasedUserData.cpp index 7ddca9752e..b1117d672e 100644 --- a/ApplicationCode/FileInterface/RifColumnBasedUserData.cpp +++ b/ApplicationCode/FileInterface/RifColumnBasedUserData.cpp @@ -163,7 +163,7 @@ void RifColumnBasedUserData::buildTimeStepsAndMappings() { RifEclipseSummaryAddress sumAddress = ci.summaryAddress; - m_allResultAddresses.push_back(sumAddress); + m_allResultAddresses.insert(sumAddress); m_mapFromAddressToTimeStepIndex[sumAddress] = m_timeSteps.size() - 1; m_mapFromAddressToResultIndex[sumAddress] = std::make_pair(tableIndex, columIndex); diff --git a/ApplicationCode/FileInterface/RifCsvUserData.cpp b/ApplicationCode/FileInterface/RifCsvUserData.cpp index 21b85d301f..68172f7170 100644 --- a/ApplicationCode/FileInterface/RifCsvUserData.cpp +++ b/ApplicationCode/FileInterface/RifCsvUserData.cpp @@ -158,7 +158,7 @@ void RifCsvUserData::buildTimeStepsAndMappings() { RifEclipseSummaryAddress sumAddress = ci.summaryAddress; - m_allResultAddresses.push_back(sumAddress); + m_allResultAddresses.insert(sumAddress); if (sumAddress.isErrorResult()) m_allErrorAddresses.insert(sumAddress); m_mapFromAddressToTimeStepIndex[sumAddress] = m_timeSteps.size() - 1; diff --git a/ApplicationCode/FileInterface/RifDerivedEnsembleReader.cpp b/ApplicationCode/FileInterface/RifDerivedEnsembleReader.cpp new file mode 100644 index 0000000000..bf8d460572 --- /dev/null +++ b/ApplicationCode/FileInterface/RifDerivedEnsembleReader.cpp @@ -0,0 +1,73 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// 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 "RifDerivedEnsembleReader.h" + +#include "RimDerivedEnsembleCase.h" + + +static const std::vector EMPTY_TIME_STEPS_VECTOR; + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifDerivedEnsembleReader::RifDerivedEnsembleReader(RimDerivedEnsembleCase* derivedCase) +{ + CVF_ASSERT(derivedCase); + + m_derivedCase = derivedCase; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const std::vector& RifDerivedEnsembleReader::timeSteps(const RifEclipseSummaryAddress& resultAddress) const +{ + if (!resultAddress.isValid()) return EMPTY_TIME_STEPS_VECTOR; + if (m_derivedCase->needsCalculation(resultAddress)) + { + m_derivedCase->calculate(resultAddress); + } + return m_derivedCase->timeSteps(resultAddress); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifDerivedEnsembleReader::values(const RifEclipseSummaryAddress& resultAddress, std::vector* values) const +{ + if (!resultAddress.isValid()) return false; + + if (m_derivedCase->needsCalculation(resultAddress)) + { + m_derivedCase->calculate(resultAddress); + } + auto dataValues = m_derivedCase->values(resultAddress); + values->clear(); + values->reserve(dataValues.size()); + for (auto val : dataValues) values->push_back(val); + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::string RifDerivedEnsembleReader::unitName(const RifEclipseSummaryAddress& resultAddress) const +{ + return ""; +} diff --git a/ApplicationCode/FileInterface/RifDerivedEnsembleReader.h b/ApplicationCode/FileInterface/RifDerivedEnsembleReader.h new file mode 100644 index 0000000000..39a10dc7bf --- /dev/null +++ b/ApplicationCode/FileInterface/RifDerivedEnsembleReader.h @@ -0,0 +1,42 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// 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 "RifEclipseSummaryAddress.h" +#include "RifSummaryReaderInterface.h" + +class RimDerivedEnsembleCase; + + +//================================================================================================== +/// +//================================================================================================== +class RifDerivedEnsembleReader : public RifSummaryReaderInterface +{ +public: + RifDerivedEnsembleReader(RimDerivedEnsembleCase* derivedCase); + + 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: + RimDerivedEnsembleCase* m_derivedCase; +}; diff --git a/ApplicationCode/FileInterface/RifEclipseSummaryTools.cpp b/ApplicationCode/FileInterface/RifEclipseSummaryTools.cpp index bf4201a478..a6c9efafea 100644 --- a/ApplicationCode/FileInterface/RifEclipseSummaryTools.cpp +++ b/ApplicationCode/FileInterface/RifEclipseSummaryTools.cpp @@ -167,7 +167,7 @@ QString RifEclipseSummaryTools::findGridCaseFileFromSummaryHeaderFile(const QStr //-------------------------------------------------------------------------------------------------- void RifEclipseSummaryTools::dumpMetaData(RifSummaryReaderInterface* readerEclipseSummary) { - std::vector addresses = readerEclipseSummary->allResultAddresses(); + std::set addresses = readerEclipseSummary->allResultAddresses(); for (int category = 0; category < RifEclipseSummaryAddress::SUMMARY_BLOCK_LGR; category++) { diff --git a/ApplicationCode/FileInterface/RifEnsembleStatisticsReader.cpp b/ApplicationCode/FileInterface/RifEnsembleStatisticsReader.cpp index 2c801dda24..8e57ac02bd 100644 --- a/ApplicationCode/FileInterface/RifEnsembleStatisticsReader.cpp +++ b/ApplicationCode/FileInterface/RifEnsembleStatisticsReader.cpp @@ -32,7 +32,7 @@ RifEnsembleStatisticsReader::RifEnsembleStatisticsReader(RimEnsembleStatisticsCa m_ensembleStatCase = ensStatCase; - m_allResultAddresses = std::vector( + m_allResultAddresses = std::set( { RifEclipseSummaryAddress::ensembleStatisticsAddress(ENSEMBLE_STAT_P10_QUANTITY_NAME, ""), RifEclipseSummaryAddress::ensembleStatisticsAddress(ENSEMBLE_STAT_P50_QUANTITY_NAME, ""), @@ -46,7 +46,7 @@ RifEnsembleStatisticsReader::RifEnsembleStatisticsReader(RimEnsembleStatisticsCa //-------------------------------------------------------------------------------------------------- const std::vector& RifEnsembleStatisticsReader::timeSteps(const RifEclipseSummaryAddress& resultAddress) const { - if (!resultAddress.isValid()) return EMPTY_TIME_STEPS_VECTOR; + if (!validateAddress(resultAddress)) return EMPTY_TIME_STEPS_VECTOR; return m_ensembleStatCase->timeSteps(); } @@ -78,7 +78,7 @@ bool RifEnsembleStatisticsReader::values(const RifEclipseSummaryAddress& resultA //-------------------------------------------------------------------------------------------------- std::string RifEnsembleStatisticsReader::unitName(const RifEclipseSummaryAddress& resultAddress) const { - return "(RifEnsembleStatisticsReader::unitName)"; + return ""; } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/FileInterface/RifKeywordVectorUserData.cpp b/ApplicationCode/FileInterface/RifKeywordVectorUserData.cpp index bc977f4a7a..218580f901 100644 --- a/ApplicationCode/FileInterface/RifKeywordVectorUserData.cpp +++ b/ApplicationCode/FileInterface/RifKeywordVectorUserData.cpp @@ -179,7 +179,7 @@ bool RifKeywordVectorUserData::parse(const QString& data, const QString& customW -1, false); - m_allResultAddresses.push_back(addr); + m_allResultAddresses.insert(addr); m_mapFromAddressToTimeIndex[addr] = timeStepIndexIterator->second; m_mapFromAddressToVectorIndex[addr] = i; diff --git a/ApplicationCode/FileInterface/RifReaderEclipseSummary.cpp b/ApplicationCode/FileInterface/RifReaderEclipseSummary.cpp index 7ea757cce5..4dc8b357a6 100644 --- a/ApplicationCode/FileInterface/RifReaderEclipseSummary.cpp +++ b/ApplicationCode/FileInterface/RifReaderEclipseSummary.cpp @@ -441,7 +441,7 @@ void RifReaderEclipseSummary::buildMetaData() { const smspec_node_type * ertSumVarNode = ecl_smspec_iget_node(m_ecl_SmSpec, i); RifEclipseSummaryAddress addr = addressFromErtSmSpecNode(ertSumVarNode); - m_allResultAddresses.push_back(addr); + m_allResultAddresses.insert(addr); m_resultAddressToErtNodeIdx[addr] = i; } } diff --git a/ApplicationCode/FileInterface/RifReaderObservedData.cpp b/ApplicationCode/FileInterface/RifReaderObservedData.cpp index 1dda556a70..c99f3a0099 100644 --- a/ApplicationCode/FileInterface/RifReaderObservedData.cpp +++ b/ApplicationCode/FileInterface/RifReaderObservedData.cpp @@ -79,7 +79,7 @@ bool RifReaderObservedData::open(const QString& headerFileName, m_allResultAddresses.clear(); for (auto s : m_asciiParser->tableData().columnInfos()) { - m_allResultAddresses.push_back(s.summaryAddress); + m_allResultAddresses.insert(s.summaryAddress); } } @@ -95,12 +95,14 @@ bool RifReaderObservedData::values(const RifEclipseSummaryAddress& resultAddress { size_t columnIndex = m_allResultAddresses.size(); - for (size_t i = 0; i < m_allResultAddresses.size(); i++) + int i = 0; + for(auto& address : m_allResultAddresses) { - if (resultAddress == m_allResultAddresses[i]) + if (address == resultAddress) { columnIndex = i; } + i++; } if (columnIndex != m_allResultAddresses.size()) diff --git a/ApplicationCode/FileInterface/RifSummaryReaderInterface.cpp b/ApplicationCode/FileInterface/RifSummaryReaderInterface.cpp index 0124ec1614..af6c8f96a5 100644 --- a/ApplicationCode/FileInterface/RifSummaryReaderInterface.cpp +++ b/ApplicationCode/FileInterface/RifSummaryReaderInterface.cpp @@ -26,7 +26,7 @@ //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -const std::vector& RifSummaryReaderInterface::allResultAddresses() const +const std::set& RifSummaryReaderInterface::allResultAddresses() const { return m_allResultAddresses; } diff --git a/ApplicationCode/FileInterface/RifSummaryReaderInterface.h b/ApplicationCode/FileInterface/RifSummaryReaderInterface.h index 357269cd2d..f373f20f8d 100644 --- a/ApplicationCode/FileInterface/RifSummaryReaderInterface.h +++ b/ApplicationCode/FileInterface/RifSummaryReaderInterface.h @@ -25,6 +25,7 @@ #include #include #include +#include class QDateTime; @@ -37,7 +38,7 @@ class RifSummaryReaderInterface : public cvf::Object { public: bool hasAddress(const RifEclipseSummaryAddress& resultAddress) const; - const std::vector& allResultAddresses() const; + const std::set& allResultAddresses() const; RifEclipseSummaryAddress errorAddress(const RifEclipseSummaryAddress& resultAddress) const; virtual const std::vector& timeSteps(const RifEclipseSummaryAddress& resultAddress) const = 0; @@ -51,6 +52,6 @@ public: static std::vector fromTimeT(const std::vector& timeSteps); protected: - std::vector m_allResultAddresses; // Result and error addresses + std::set m_allResultAddresses; // Result and error addresses std::set m_allErrorAddresses; // Error addresses }; diff --git a/ApplicationCode/ProjectDataModel/RimContextCommandBuilder.cpp b/ApplicationCode/ProjectDataModel/RimContextCommandBuilder.cpp index 00b4feca36..909c4d157f 100644 --- a/ApplicationCode/ProjectDataModel/RimContextCommandBuilder.cpp +++ b/ApplicationCode/ProjectDataModel/RimContextCommandBuilder.cpp @@ -487,6 +487,7 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection() menuBuilder << "RicImportSummaryCasesFeature"; menuBuilder << "RicImportSummaryGroupFeature"; menuBuilder << "RicImportEnsembleFeature"; + menuBuilder << "RicNewDerivedEnsembleFeature"; } else if (dynamic_cast(uiItem)) { @@ -497,6 +498,7 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection() menuBuilder << "RicImportEnsembleFeature"; menuBuilder.subMenuEnd(); menuBuilder.addSeparator(); + menuBuilder << "RicNewDerivedEnsembleFeature"; menuBuilder << "RicNewSummaryPlotFeature"; menuBuilder << "RicNewSummaryCrossPlotFeature"; menuBuilder.addSeparator(); @@ -676,6 +678,7 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection() menuBuilder << "RicShowPlotDataFeature"; menuBuilder << "RicShowTotalAllocationDataFeature"; + menuBuilder << "RicNewDerivedEnsembleFeature"; menuBuilder << "RicNewSummaryPlotFeature"; menuBuilder << "RicNewSummaryCrossPlotFeature"; menuBuilder << "RicSummaryCurveSwitchAxisFeature"; diff --git a/ApplicationCode/ProjectDataModel/RimProject.cpp b/ApplicationCode/ProjectDataModel/RimProject.cpp index 76b8eb90a4..fec9284de2 100644 --- a/ApplicationCode/ProjectDataModel/RimProject.cpp +++ b/ApplicationCode/ProjectDataModel/RimProject.cpp @@ -624,6 +624,15 @@ std::vector RimProject::summaryGroups() const return groups; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimSummaryCaseMainCollection* RimProject::firstSummaryCaseMainCollection() const +{ + if (oilFields.empty()) return nullptr; + return oilFields[0]->summaryCaseMainCollection; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/ProjectDataModel/RimProject.h b/ApplicationCode/ProjectDataModel/RimProject.h index 22e1091435..1f83a39d3c 100644 --- a/ApplicationCode/ProjectDataModel/RimProject.h +++ b/ApplicationCode/ProjectDataModel/RimProject.h @@ -48,6 +48,7 @@ class RimOilField; class RimScriptCollection; class RimSummaryCase; class RimSummaryCaseCollection; +class RimSummaryCaseMainCollection; class Rim3dView; class RimGridView; class RimViewLinker; @@ -108,7 +109,8 @@ public: std::vector allSummaryCases() const; std::vector summaryGroups() const; - + RimSummaryCaseMainCollection* firstSummaryCaseMainCollection() const; + void allVisibleViews(std::vector& views); void allVisibleGridViews(std::vector& views); void allNotLinkedViews(std::vector& views); diff --git a/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake b/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake index 083f34d4d8..c9f905f1df 100644 --- a/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake +++ b/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake @@ -36,6 +36,8 @@ ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveFilter.h ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveFilterCollection.h ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatistics.h ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatisticsCase.h +${CMAKE_CURRENT_LIST_DIR}/RimDerivedEnsembleCase.h +${CMAKE_CURRENT_LIST_DIR}/RimDerivedEnsembleCaseCollection.h ) set (SOURCE_GROUP_SOURCE_FILES @@ -75,6 +77,8 @@ ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveFilter.cpp ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveFilterCollection.cpp ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatistics.cpp ${CMAKE_CURRENT_LIST_DIR}/RimEnsembleStatisticsCase.cpp +${CMAKE_CURRENT_LIST_DIR}/RimDerivedEnsembleCase.cpp +${CMAKE_CURRENT_LIST_DIR}/RimDerivedEnsembleCaseCollection.cpp ) list(APPEND CODE_HEADER_FILES diff --git a/ApplicationCode/ProjectDataModel/Summary/RimCalculatedSummaryCurveReader.cpp b/ApplicationCode/ProjectDataModel/Summary/RimCalculatedSummaryCurveReader.cpp index 2fd8534f73..1dda139f88 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimCalculatedSummaryCurveReader.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimCalculatedSummaryCurveReader.cpp @@ -86,7 +86,7 @@ void RifCalculatedSummaryCurveReader::buildMetaData() for (RimSummaryCalculation* calc : m_calculationCollection->calculations()) { - m_allResultAddresses.push_back(RifEclipseSummaryAddress::calculatedAddress(calc->description().toStdString())); + m_allResultAddresses.insert(RifEclipseSummaryAddress::calculatedAddress(calc->description().toStdString())); } } diff --git a/ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCase.cpp b/ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCase.cpp new file mode 100644 index 0000000000..6a1ec55686 --- /dev/null +++ b/ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCase.cpp @@ -0,0 +1,220 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2016- 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 "RimDerivedEnsembleCase.h" + +#include "RiaSummaryTools.h" +#include "RiaTimeHistoryCurveMerger.h" + +#include "RifDerivedEnsembleReader.h" + +#include "RimDerivedEnsembleCaseCollection.h" +#include "RimMainPlotCollection.h" +#include "RimOilField.h" +#include "RimProject.h" +#include "RimSummaryCaseMainCollection.h" +#include "RimSummaryPlotCollection.h" +#include "RimSummaryCaseCollection.h" + +#include "cvfAssert.h" + +#include + +CAF_PDM_ABSTRACT_SOURCE_INIT(RimDerivedEnsembleCase, "RimDerivedEnsembleCase"); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +static const std::vector EMPTY_TIME_STEPS_VECTOR; +static const std::vector EMPTY_VALUES_VECTOR; + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimDerivedEnsembleCase::RimDerivedEnsembleCase() : m_summaryCase1(nullptr), m_summaryCase2(nullptr) +{ + CAF_PDM_InitObject("Summary Case",":/SummaryCase16x16.png","",""); + CAF_PDM_InitFieldNoDefault(&m_summaryCase1, "SummaryCase1", "SummaryCase1", "", "", ""); + CAF_PDM_InitFieldNoDefault(&m_summaryCase2, "SummaryCase2", "SummaryCase2", "", "", ""); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCase::setInUse(bool inUse) +{ + m_inUse = inUse; + + if (!m_inUse) + { + m_summaryCase1 = nullptr; + m_summaryCase2 = nullptr; + m_data.clear(); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimDerivedEnsembleCase::isInUse() const +{ + return m_inUse; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCase::setSummaryCases(RimSummaryCase* sumCase1, RimSummaryCase* sumCase2) +{ + if (!sumCase1 || !sumCase2) return; + m_summaryCase1 = sumCase1; + m_summaryCase2 = sumCase2; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimDerivedEnsembleCase::needsCalculation(const RifEclipseSummaryAddress& address) const +{ + return m_data.count(address) == 0; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const std::vector& RimDerivedEnsembleCase::timeSteps(const RifEclipseSummaryAddress& address) const +{ + if (m_data.count(address) == 0) return EMPTY_TIME_STEPS_VECTOR; + return m_data.at(address).first; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const std::vector& RimDerivedEnsembleCase::values(const RifEclipseSummaryAddress& address) const +{ + if (m_data.count(address) == 0) return EMPTY_VALUES_VECTOR; + return m_data.at(address).second; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCase::calculate(const RifEclipseSummaryAddress& address) +{ + clearData(address); + + RifSummaryReaderInterface* reader1 = m_summaryCase1 ? m_summaryCase1->summaryReader() : nullptr; + RifSummaryReaderInterface* reader2 = m_summaryCase2 ? m_summaryCase2->summaryReader() : nullptr; + if (!reader1 || !reader2 || !parentEnsemble()) return; + + RiaTimeHistoryCurveMerger merger; + std::vector values1; + std::vector values2; + DerivedEnsembleOperator op = parentEnsemble()->op(); + + reader1->values(address, &values1); + reader2->values(address, &values2); + + merger.addCurveData(values1, reader1->timeSteps(address)); + merger.addCurveData(values2, reader2->timeSteps(address)); + merger.computeInterpolatedValues(); + + size_t sampleCount = merger.allTimeSteps().size(); + std::vector calculatedValues; + calculatedValues.reserve(sampleCount); + for (size_t i = 0; i < sampleCount; i++) + { + if (op == DERIVED_ENSEMBLE_SUB) + { + calculatedValues.push_back(values1[i] - values2[i]); + } + else if (op == DERIVED_ENSEMBLE_ADD) + { + calculatedValues.push_back(values1[i] + values2[i]); + } + } + + auto& dataItem = m_data[address]; + dataItem.first = merger.allTimeSteps(); + dataItem.second = calculatedValues; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimDerivedEnsembleCase::caseName() +{ + auto case1Name = m_summaryCase1->caseName(); + auto case2Name = m_summaryCase2->caseName(); + + if (case1Name == case2Name) return case1Name; + else return QString("%1/%2").arg(case1Name).arg(case2Name); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCase::createSummaryReaderInterface() +{ + m_reader.reset(new RifDerivedEnsembleReader(this)); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifSummaryReaderInterface* RimDerivedEnsembleCase::summaryReader() +{ + return m_reader.get(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCase::updateFilePathsFromProjectPath(const QString& newProjectPath, const QString& oldProjectPath) +{ + // NOP +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimDerivedEnsembleCaseCollection * RimDerivedEnsembleCase::parentEnsemble() const +{ + RimDerivedEnsembleCaseCollection* ensemble; + firstAncestorOrThisOfType(ensemble); + return ensemble; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::pair, std::vector> RimDerivedEnsembleCase::lookupCachedData(const RifEclipseSummaryAddress& address) +{ + auto itr = m_data.find(address); + if (itr == m_data.end()) return std::make_pair(std::vector(), std::vector()); + return itr->second; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCase::clearData(const RifEclipseSummaryAddress& address) +{ + m_data.erase(address); +} diff --git a/ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCase.h b/ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCase.h new file mode 100644 index 0000000000..651bd65aee --- /dev/null +++ b/ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCase.h @@ -0,0 +1,78 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2016 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 "RimSummaryCase.h" + +#include "cafPdmField.h" +#include "cafPdmObject.h" +#include "cafPdmPtrField.h" + +#include + +class RifEclipseSummaryAddress; +class RifSummaryReaderInterface; +class RifDerivedEnsembleReader; +class RimDerivedEnsembleCaseCollection; + +//================================================================================================== +/// +//================================================================================================== +enum DerivedEnsembleOperator +{ + DERIVED_ENSEMBLE_SUB, + DERIVED_ENSEMBLE_ADD +}; + +//================================================================================================== +// +//================================================================================================== + +class RimDerivedEnsembleCase : public RimSummaryCase +{ + CAF_PDM_HEADER_INIT; +public: + RimDerivedEnsembleCase(); + + void setInUse(bool inUse); + bool isInUse() const; + void setSummaryCases(RimSummaryCase* sumCase1, RimSummaryCase* sumCase2); + bool needsCalculation(const RifEclipseSummaryAddress& address) const; + const std::vector& timeSteps(const RifEclipseSummaryAddress& address) const; + const std::vector& values(const RifEclipseSummaryAddress& address) const; + + void calculate(const RifEclipseSummaryAddress& address); + + virtual QString caseName() override; + virtual void createSummaryReaderInterface() override; + virtual RifSummaryReaderInterface* summaryReader() override; + virtual void updateFilePathsFromProjectPath(const QString& newProjectPath, const QString& oldProjectPath) override; + + RimDerivedEnsembleCaseCollection* parentEnsemble() const; + +private: + std::pair, std::vector> lookupCachedData(const RifEclipseSummaryAddress& address); + void clearData(const RifEclipseSummaryAddress& address); + + std::unique_ptr m_reader; + + bool m_inUse; + caf::PdmPtrField m_summaryCase1; + caf::PdmPtrField m_summaryCase2; + std::map, std::vector>> m_data; +}; diff --git a/ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCaseCollection.cpp b/ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCaseCollection.cpp new file mode 100644 index 0000000000..8b813d3813 --- /dev/null +++ b/ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCaseCollection.cpp @@ -0,0 +1,440 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// 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 "RiaApplication.h" +#include "RiaTimeHistoryCurveMerger.h" + +#include "RimDerivedEnsembleCaseCollection.h" +#include "RimDerivedEnsembleCase.h" +#include "RimProject.h" +#include "RimSummaryCaseCollection.h" +#include "RimSummaryCaseMainCollection.h" + +#include "RifSummaryReaderInterface.h" + +#include + +#include + +namespace caf +{ + template<> + void caf::AppEnum::setUp() + { + addItem(DERIVED_ENSEMBLE_SUB, "Sub", "-"); + addItem(DERIVED_ENSEMBLE_ADD, "Add", "+"); + setDefault(DERIVED_ENSEMBLE_SUB); + } +} + +CAF_PDM_SOURCE_INIT(RimDerivedEnsembleCaseCollection, "RimDerivedEnsembleCaseCollection"); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimDerivedEnsembleCaseCollection::RimDerivedEnsembleCaseCollection() +{ + CAF_PDM_InitObject("Derived Ensemble", ":/SummaryEnsemble16x16.png", "", ""); + + CAF_PDM_InitFieldNoDefault(&m_ensemble1, "Ensemble1", "Ensemble 1", "", "", ""); + m_ensemble1.uiCapability()->setUiTreeChildrenHidden(true); + m_ensemble1.uiCapability()->setAutoAddingOptionFromValue(false); + + CAF_PDM_InitFieldNoDefault(&m_ensemble2, "Ensemble2", "Ensemble 2", "", "", ""); + m_ensemble1.uiCapability()->setUiTreeChildrenHidden(true); + m_ensemble2.uiCapability()->setAutoAddingOptionFromValue(false); + + CAF_PDM_InitFieldNoDefault(&m_operator, "Operator", "Operator", "", "", ""); + + CAF_PDM_InitField(&m_swapEnsemblesButton, "SwapEnsembles", false, "SwapEnsembles", "", "", ""); + m_swapEnsemblesButton.uiCapability()->setUiEditorTypeName(caf::PdmUiPushButtonEditor::uiEditorTypeName()); + m_swapEnsemblesButton.uiCapability()->setUiLabelPosition(caf::PdmUiItemInfo::HIDDEN); + m_swapEnsemblesButton.xmlCapability()->disableIO(); + + CAF_PDM_InitField(&m_caseCount, "CaseCount", 0, "Number of Cases", "", "", ""); + m_caseCount.uiCapability()->setUiReadOnly(true); + + // Do not show child cases + uiCapability()->setUiTreeChildrenHidden(true); + + // Do not store child cases to project file + m_cases.xmlCapability()->disableIO(); + + setNameAsReadOnly(); + setName("Derived Ensemble"); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimDerivedEnsembleCaseCollection::~RimDerivedEnsembleCaseCollection() +{ + +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCaseCollection::setEnsemble1(RimSummaryCaseCollection* ensemble) +{ + m_ensemble1 = ensemble; + updateAutoName(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCaseCollection::setEnsemble2(RimSummaryCaseCollection* ensemble) +{ + m_ensemble2 = ensemble; + updateAutoName(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimDerivedEnsembleCaseCollection::allSummaryCases() const +{ + std::vector cases; + for (auto sumCase : allDerivedCases(true)) cases.push_back(sumCase); + return cases; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::set RimDerivedEnsembleCaseCollection::calculateUnionOfSummaryAddresses() const +{ + std::set addresses; + if (!m_ensemble1 || !m_ensemble2) return addresses; + + addresses = m_ensemble1->calculateUnionOfSummaryAddresses(); + auto addrs2 = m_ensemble2->calculateUnionOfSummaryAddresses(); + addresses.insert(addrs2.begin(), addrs2.end()); + return addresses; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCaseCollection::updateDerivedEnsembleCases() +{ + if (!m_ensemble1 || !m_ensemble2) return; + + setAllCasesNotInUse(); + + const auto cases1 = m_ensemble1->allSummaryCases(); + const auto cases2 = m_ensemble2->allSummaryCases(); + + for (auto& sumCase1 : cases1) + { + auto crp = sumCase1->caseRealizationParameters(); + if (!crp) continue; + + const auto& sumCase2 = findCaseByParametersHash(cases2, crp->parametersHash()); + if (!sumCase2) continue; + + auto derivedCase = firstCaseNotInUse(); + derivedCase->createSummaryReaderInterface(); + derivedCase->setSummaryCases(sumCase1, sumCase2); + derivedCase->setCaseRealizationParameters(crp); + derivedCase->setInUse(true); + } + + // If other derived ensembles are referring to this ensemble, update their cases as well + for (auto referring : findReferringEnsembles()) + { + referring->updateDerivedEnsembleCases(); + } + + deleteCasesNoInUse(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimDerivedEnsembleCaseCollection::hasCaseReference(const RimSummaryCase* sumCase) const +{ + for (auto currCase : m_ensemble1->allSummaryCases()) + { + if (currCase == sumCase) return true; + } + for (auto currCase : m_ensemble2->allSummaryCases()) + { + if (currCase == sumCase) return true; + } + return false; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCaseCollection::onLoadDataAndUpdate() +{ + updateDerivedEnsembleCases(); + updateReferringCurveSets(); + updateConnectedEditors(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QList RimDerivedEnsembleCaseCollection::calculateValueOptions(const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly) +{ + QList options; + + if (fieldNeedingOptions == &m_ensemble1 || fieldNeedingOptions == &m_ensemble2) + { + for (auto ensemble : allEnsembles()) + { + if(ensemble != this) options.push_back(caf::PdmOptionItemInfo(ensemble->name(), ensemble)); + } + } + else if (fieldNeedingOptions == &m_caseCount) + { + m_caseCount = (int)m_cases.size(); + } + return options; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCaseCollection::defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) +{ + RimSummaryCaseCollection::defineUiOrdering(uiConfigName, uiOrdering); + + uiOrdering.add(&m_caseCount); + uiOrdering.add(&m_ensemble1); + uiOrdering.add(&m_operator); + uiOrdering.add(&m_ensemble2); + uiOrdering.add(&m_swapEnsemblesButton); + + uiOrdering.skipRemainingFields(true); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCaseCollection::fieldChangedByUi(const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue) +{ + bool doUpdate = false; + bool doUpdateCases = false; + bool doClearAllData = false; + + if (changedField == &m_ensemble1 || changedField == &m_ensemble2) + { + doUpdate = true; + doUpdateCases = true; + } + else if (changedField == &m_operator) + { + doUpdate = true; + doUpdateCases = true; + } + else if (changedField == &m_swapEnsemblesButton) + { + m_swapEnsemblesButton = false; + auto temp = m_ensemble1(); + m_ensemble1 = m_ensemble2(); + m_ensemble2 = temp; + + doUpdate = true; + doUpdateCases = true; + } + + if (doUpdate) + { + updateAutoName(); + //if (doClearAllData) clearAllData(); + + if (doUpdateCases) + { + updateDerivedEnsembleCases(); + updateConnectedEditors(); + } + + updateReferringCurveSets(); + + // If other derived ensembles are referring to this ensemble, update their cases as well + for (auto refering : findReferringEnsembles()) + { + refering->updateReferringCurveSets(); + } + + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCaseCollection::defineEditorAttribute(const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute) +{ + if (field == &m_swapEnsemblesButton) + { + caf::PdmUiPushButtonEditorAttribute* attrib = dynamic_cast(attribute); + if (attrib) + { + attrib->m_buttonText = "Swap Ensembles"; + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCaseCollection::setAllCasesNotInUse() +{ + for (auto derCase : allDerivedCases(true)) derCase->setInUse(false); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCaseCollection::deleteCasesNoInUse() +{ + std::vector inactiveCases; + auto allCases = allDerivedCases(false); + std::copy_if(allCases.begin(), allCases.end(), std::back_inserter(inactiveCases), [](RimDerivedEnsembleCase* derCase) { return !derCase->isInUse(); }); + + for (auto derCase : inactiveCases) + { + removeCase(derCase); + delete derCase; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimDerivedEnsembleCase* RimDerivedEnsembleCaseCollection::firstCaseNotInUse() +{ + auto allCases = allDerivedCases(false); + auto itr = std::find_if(allCases.begin(), allCases.end(), [](RimDerivedEnsembleCase* derCase) { return !derCase->isInUse(); }); + if (itr != allCases.end()) + { + return *itr; + } + + // If no active case was found, add a new case to the collection + auto newCase = new RimDerivedEnsembleCase(); + m_cases.push_back(newCase); + return newCase; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimDerivedEnsembleCaseCollection::allDerivedCases(bool activeOnly) const +{ + std::vector activeCases; + for (auto sumCase : RimSummaryCaseCollection::allSummaryCases()) + { + auto derivedCase = dynamic_cast(sumCase); + if (derivedCase && (!activeOnly || derivedCase->isInUse())) + { + activeCases.push_back(derivedCase); + } + } + return activeCases; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimDerivedEnsembleCaseCollection::updateAutoName() +{ + QString op = caf::AppEnum::uiText(m_operator()); + + auto derivedEnsemble1 = dynamic_cast(m_ensemble1()); + auto derivedEnsemble2 = dynamic_cast(m_ensemble2()); + bool isDerived1 = derivedEnsemble1 != nullptr; + bool isDerived2 = derivedEnsemble2 != nullptr; + + QString name = + (isDerived1 ? "(" : "") + + (m_ensemble1 ? m_ensemble1->name() : "") + + (isDerived1 ? ")" : "") + + " " + op + " " + + (isDerived2 ? "(" : "") + + (m_ensemble2 ? m_ensemble2->name() : "") + + (isDerived2 ? ")" : ""); + setName(name); + + // If other derived ensembles are referring to this ensemble, update theirs name as well + for (auto refering : findReferringEnsembles()) + { + refering->updateAutoName(); + refering->updateConnectedEditors(); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimSummaryCase* RimDerivedEnsembleCaseCollection::findCaseByParametersHash(const std::vector& cases, size_t hash) const +{ + for (auto sumCase : cases) + { + auto ensembleParameters = sumCase->caseRealizationParameters(); + if (ensembleParameters && ensembleParameters->parametersHash() == hash) return sumCase; + } + return nullptr; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimDerivedEnsembleCaseCollection::findReferringEnsembles() const +{ + std::vector referringEnsembles; + RimSummaryCaseMainCollection* mainColl; + + firstAncestorOrThisOfType(mainColl); + if (mainColl) + { + for (auto group : mainColl->summaryCaseCollections()) + { + auto derivedEnsemble = dynamic_cast(group); + if (derivedEnsemble) + { + if (derivedEnsemble->m_ensemble1() == this || derivedEnsemble->m_ensemble2() == this) + { + referringEnsembles.push_back(derivedEnsemble); + } + } + } + } + return referringEnsembles; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimDerivedEnsembleCaseCollection::allEnsembles() const +{ + std::vector ensembles; + + auto project = RiaApplication::instance()->project(); + + for (auto group : project->summaryGroups()) + { + if (group->isEnsemble()) ensembles.push_back(group); + } + return ensembles; +} diff --git a/ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCaseCollection.h b/ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCaseCollection.h new file mode 100644 index 0000000000..5913cd1d75 --- /dev/null +++ b/ApplicationCode/ProjectDataModel/Summary/RimDerivedEnsembleCaseCollection.h @@ -0,0 +1,85 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2016- 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 "RifEclipseSummaryAddress.h" + +#include "RimDerivedEnsembleCase.h" +#include "RimSummaryCaseCollection.h" + +#include "cafPdmChildArrayField.h" +#include "cafPdmField.h" +#include "cafPdmPtrField.h" +#include "cafPdmObject.h" +#include "cafPdmProxyValueField.h" + +#include + +class RimSummaryCase; + +//================================================================================================== +/// +//================================================================================================== +class RimDerivedEnsembleCaseCollection : public RimSummaryCaseCollection +{ + CAF_PDM_HEADER_INIT; + +public: + RimDerivedEnsembleCaseCollection(); + virtual ~RimDerivedEnsembleCaseCollection(); + + RimSummaryCaseCollection* ensemble1() const { return m_ensemble1; } + RimSummaryCaseCollection* ensemble2() const { return m_ensemble2; } + DerivedEnsembleOperator op() const { return m_operator(); } + + void setEnsemble1(RimSummaryCaseCollection* ensemble); + void setEnsemble2(RimSummaryCaseCollection* ensemble); + + virtual std::vector allSummaryCases() const override; + virtual std::set calculateUnionOfSummaryAddresses() const override; + void updateDerivedEnsembleCases(); + bool hasCaseReference(const RimSummaryCase* sumCase) const; + + virtual void onLoadDataAndUpdate() override; + +private: + virtual QList calculateValueOptions(const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly) override; + virtual void defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) override; + virtual void fieldChangedByUi(const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue) override; + virtual void defineEditorAttribute(const caf::PdmFieldHandle* field, QString uiConfigName, + caf::PdmUiEditorAttribute* attribute) override; + + void setAllCasesNotInUse(); + void deleteCasesNoInUse(); + RimDerivedEnsembleCase* firstCaseNotInUse(); + std::vector allDerivedCases(bool activeOnly) const; + void updateAutoName(); + RimSummaryCase* findCaseByParametersHash(const std::vector& cases, size_t hash) const; + std::vector findReferringEnsembles() const; + +private: + std::vector allEnsembles() const; + +private: + caf::PdmPtrField m_ensemble1; + caf::PdmPtrField m_ensemble2; + caf::PdmField> m_operator; + caf::PdmField m_swapEnsemblesButton; + caf::PdmField m_caseCount; +}; diff --git a/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp index db07f8933f..1f391459ef 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp @@ -29,6 +29,7 @@ #include "RigStatisticsMath.h" #include "RiaTimeHistoryCurveMerger.h" +#include "RimDerivedEnsembleCaseCollection.h" #include "RimEnsembleCurveFilter.h" #include "RimEnsembleCurveFilterCollection.h" #include "RimEnsembleCurveSetCollection.h" @@ -735,7 +736,7 @@ void RimEnsembleCurveSet::appendOptionItemsForSummaryAddresses(QListallSummaryCases()) { RifSummaryReaderInterface* reader = summaryCase->summaryReader(); - const std::vector& addrs = reader ? reader->allResultAddresses() : std::vector(); + const std::set& addrs = reader ? reader->allResultAddresses() : std::set(); for (auto& addr : addrs) { diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.cpp b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.cpp index 10827885b2..a48d0b291e 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.cpp @@ -18,6 +18,7 @@ #include "RimSummaryCaseCollection.h" +#include "RimDerivedEnsembleCaseCollection.h" #include "RimEnsembleCurveSet.h" #include "RimGridSummaryCase.h" #include "RimProject.h" @@ -68,13 +69,34 @@ void RimSummaryCaseCollection::removeCase(RimSummaryCase* summaryCase) updateReferringCurveSets(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryCaseCollection::deleteAllCases() +{ + m_cases.deleteAllChildObjects(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimSummaryCaseCollection::addCase(RimSummaryCase* summaryCase) +void RimSummaryCaseCollection::addCase(RimSummaryCase* summaryCase, bool updateCurveSets) { m_cases.push_back(summaryCase); - updateReferringCurveSets(); + + // Update derived ensemble cases (if any) + std::vector referringObjects; + objectsWithReferringPtrFields(referringObjects); + for (auto refObj : referringObjects) + { + auto derEnsemble = dynamic_cast(refObj); + if (!derEnsemble) continue; + + derEnsemble->updateDerivedEnsembleCases(); + if (updateCurveSets) derEnsemble->updateReferringCurveSets(); + } + + if(updateCurveSets) updateReferringCurveSets(); } //-------------------------------------------------------------------------------------------------- @@ -133,11 +155,9 @@ std::set RimSummaryCaseCollection::calculateUnionOfSum if ( !reader ) continue; - const std::vector& readerAddresses = reader->allResultAddresses(); + const std::set& readerAddresses = reader->allResultAddresses(); addressUnion.insert(readerAddresses.begin(), readerAddresses.end()); - } - return addressUnion; } @@ -234,6 +254,14 @@ EnsembleParameter RimSummaryCaseCollection::ensembleParameter(const QString& par return eParam; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryCaseCollection::loadDataAndUpdate() +{ + onLoadDataAndUpdate(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -242,10 +270,18 @@ caf::PdmFieldHandle* RimSummaryCaseCollection::userDescriptionField() return &m_name; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryCaseCollection::onLoadDataAndUpdate() +{ + // NOP +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimSummaryCaseCollection::updateReferringCurveSets() const +void RimSummaryCaseCollection::updateReferringCurveSets() { // Update curve set referring to this group std::vector referringObjects; @@ -299,3 +335,20 @@ void RimSummaryCaseCollection::fieldChangedByUi(const caf::PdmFieldHandle* chang updateIcon(); } } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryCaseCollection::defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) +{ + uiOrdering.add(&m_name); + uiOrdering.skipRemainingFields(true); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryCaseCollection::setNameAsReadOnly() +{ + m_name.uiCapability()->setUiReadOnly(true); +} diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.h b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.h index 400c14a520..eaf3bd1a13 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.h +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.h @@ -65,26 +65,35 @@ public: virtual ~RimSummaryCaseCollection(); void removeCase(RimSummaryCase* summaryCase); - void addCase(RimSummaryCase* summaryCase); - std::vector allSummaryCases() const; + void deleteAllCases(); + void addCase(RimSummaryCase* summaryCase, bool updateCurveSets = true); + virtual std::vector allSummaryCases() const; void setName(const QString& name); QString name() const; bool isEnsemble() const; void setAsEnsemble(bool isEnsemble); - std::set calculateUnionOfSummaryAddresses() const; + virtual std::set calculateUnionOfSummaryAddresses() const; EnsembleParameter ensembleParameter(const QString& paramName) const; + void loadDataAndUpdate(); + private: caf::PdmFieldHandle* userDescriptionField() override; - void updateReferringCurveSets() const; QString nameAndItemCount() const; void updateIcon(); virtual void initAfterRead() override; virtual void fieldChangedByUi(const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue) override; -private: +protected: + virtual void onLoadDataAndUpdate(); + void updateReferringCurveSets(); + virtual void defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) override; + void setNameAsReadOnly(); + caf::PdmChildArrayField m_cases; + +private: caf::PdmField m_name; caf::PdmProxyValueField m_nameAndItemCount; caf::PdmField m_isEnsemble; diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseMainCollection.cpp b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseMainCollection.cpp index e1db6de4c7..c23fd8e89c 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseMainCollection.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseMainCollection.cpp @@ -21,6 +21,7 @@ #include "RifSummaryCaseRestartSelector.h" #include "RifCaseRealizationParametersReader.h" +#include "RimDerivedEnsembleCaseCollection.h" #include "RimEclipseResultCase.h" #include "RimFileSummaryCase.h" #include "RimGridSummaryCase.h" @@ -201,19 +202,43 @@ void RimSummaryCaseMainCollection::addCase(RimSummaryCase* summaryCase) //-------------------------------------------------------------------------------------------------- void RimSummaryCaseMainCollection::removeCase(RimSummaryCase* summaryCase) { + std::vector derivedEnsembles; + + // Build a list of derived ensembles that must be updated after delete + for (auto group : summaryCaseCollections()) + { + auto derEnsemble = dynamic_cast(group); + if (derEnsemble) + { + if (derEnsemble->hasCaseReference(summaryCase)) + { + derivedEnsembles.push_back(derEnsemble); + } + } + } + m_cases.removeChildObject(summaryCase); for (RimSummaryCaseCollection* summaryCaseCollection : m_caseCollections) { summaryCaseCollection->removeCase(summaryCase); } + + // Update derived ensemble cases (if any) + for (auto derEnsemble : derivedEnsembles) + { + derEnsemble->updateDerivedEnsembleCases(); + } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RimSummaryCaseMainCollection::addCaseCollection(std::vector summaryCases, const QString& collectionName, bool isEnsemble) +RimSummaryCaseCollection* RimSummaryCaseMainCollection::addCaseCollection(std::vector summaryCases, + const QString& collectionName, + bool isEnsemble, + std::function allocator) { - RimSummaryCaseCollection* summaryCaseCollection = new RimSummaryCaseCollection(); + RimSummaryCaseCollection* summaryCaseCollection = allocator(); if(!collectionName.isEmpty()) summaryCaseCollection->setName(collectionName); summaryCaseCollection->setAsEnsemble(isEnsemble); @@ -235,6 +260,8 @@ void RimSummaryCaseMainCollection::addCaseCollection(std::vector +#include class RimGridSummaryCase; class RimSummaryCase; @@ -56,7 +57,10 @@ public: void addCase(RimSummaryCase* summaryCase); void removeCase(RimSummaryCase* summaryCase); - void addCaseCollection(std::vector summaryCases, const QString& coolectionName, bool isEnsemble); + RimSummaryCaseCollection* addCaseCollection(std::vector summaryCases, + const QString& coolectionName, + bool isEnsemble, + std::function allocator = defaultAllocator); void removeCaseCollection(RimSummaryCaseCollection* caseCollection); void loadAllSummaryCaseData(); @@ -67,6 +71,7 @@ public: private: static void loadSummaryCaseData(std::vector summaryCases); + static RimSummaryCaseCollection* defaultAllocator(); private: caf::PdmChildArrayField m_cases; diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.cpp b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.cpp index 395ba6e3a8..1b2b2fa14b 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCurve.cpp @@ -593,7 +593,7 @@ void RimSummaryCurve::appendOptionItemsForSummaryAddresses(QListsummaryReader(); if (reader) { - const std::vector allAddresses = reader->allResultAddresses(); + const std::set allAddresses = reader->allResultAddresses(); for (auto& address : allAddresses) { diff --git a/ApplicationCode/ReservoirDataModel/RigCaseRealizationParameters.cpp b/ApplicationCode/ReservoirDataModel/RigCaseRealizationParameters.cpp index 80bca9d50b..bedab5973d 100644 --- a/ApplicationCode/ReservoirDataModel/RigCaseRealizationParameters.cpp +++ b/ApplicationCode/ReservoirDataModel/RigCaseRealizationParameters.cpp @@ -17,7 +17,12 @@ ///////////////////////////////////////////////////////////////////////////////// #include "RigCaseRealizationParameters.h" + +#include +#include + #include +#include //-------------------------------------------------------------------------------------------------- /// @@ -109,3 +114,45 @@ std::map RigCaseRealizationParamet { return m_parameters; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +size_t RigCaseRealizationParameters::parameterHash(const QString& name) const +{ + auto itr = m_parameters.find(name); + if (itr == m_parameters.end() || !itr->second.isValid()) return 0; + + std::hash stringHasher; + std::hash doubleHasher; + size_t nameHash; + size_t valueHash; + + nameHash = stringHasher(name.toStdString()); + + auto value = itr->second; + if (value.isNumeric()) valueHash = doubleHasher(value.numericValue()); + else if (value.isText()) valueHash = stringHasher(value.textValue().toStdString()); + + QString s = QString::number(nameHash) + QString::number(valueHash); + return stringHasher((QString::number(nameHash) + QString::number(valueHash)).toStdString()); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +size_t RigCaseRealizationParameters::parametersHash() +{ + if (m_parametersHash == 0) + { + QStringList hashes; + for (auto param : m_parameters) + { + hashes.push_back(QString::number(parameterHash(param.first))); + } + + std::hash stringHasher; + m_parametersHash = stringHasher(hashes.join("").toStdString()); + } + return m_parametersHash; +} diff --git a/ApplicationCode/ReservoirDataModel/RigCaseRealizationParameters.h b/ApplicationCode/ReservoirDataModel/RigCaseRealizationParameters.h index 1bebe33f63..2bd5e7db78 100644 --- a/ApplicationCode/ReservoirDataModel/RigCaseRealizationParameters.h +++ b/ApplicationCode/ReservoirDataModel/RigCaseRealizationParameters.h @@ -59,12 +59,18 @@ public: QString m_textValue; }; + RigCaseRealizationParameters() : m_parametersHash(0) { } + void addParameter(const QString& name, double value); void addParameter(const QString& name, const QString& value); Value parameterValue(const QString& name); std::map parameters() const; + size_t parameterHash(const QString& name) const; + size_t parametersHash(); + private: std::map m_parameters; + size_t m_parametersHash; }; diff --git a/ApplicationCode/UserInterface/RiuSummaryCurveDefSelection.cpp b/ApplicationCode/UserInterface/RiuSummaryCurveDefSelection.cpp index 61bc3084ba..bbf8f2d9c6 100644 --- a/ApplicationCode/UserInterface/RiuSummaryCurveDefSelection.cpp +++ b/ApplicationCode/UserInterface/RiuSummaryCurveDefSelection.cpp @@ -255,42 +255,42 @@ std::vector RiuSummaryCurveDefSelection::allCurveDefi { std::set curveDefinitions; - std::set selectedAddressesFromUi = buildAddressListFromSelections(); for (SummarySource* currSource : selectedSummarySources()) { - std::vector sourceCases; + std::vector sourceSources; RimSummaryCaseCollection* ensemble = dynamic_cast(currSource); + RimSummaryCase* sumCase = dynamic_cast(currSource); + + std::set addressesFromSource; + std::vector casesFromSource; // Build case list if (ensemble) { - auto sumCases = ensemble->allSummaryCases(); - sourceCases.insert(sourceCases.end(), sumCases.begin(), sumCases.end()); + auto addresses = ensemble->calculateUnionOfSummaryAddresses(); + addressesFromSource.insert(addresses.begin(), addresses.end()); + auto ensembleCases = ensemble->allSummaryCases(); + casesFromSource.insert(casesFromSource.end(), ensembleCases.begin(), ensembleCases.end()); } else { - RimSummaryCase* sourceCase = dynamic_cast(currSource); - if (sourceCase) + RifSummaryReaderInterface* reader = sumCase ? sumCase->summaryReader() : nullptr; + if (reader) { - sourceCases.push_back(sourceCase); + addressesFromSource.insert(reader->allResultAddresses().begin(), reader->allResultAddresses().end()); + casesFromSource.push_back(sumCase); } } - for (const auto& currCase : sourceCases) + for (auto caseFromSource : casesFromSource) { - if (currCase && currCase->summaryReader()) + for (const auto& addressFromSource : addressesFromSource) { - RifSummaryReaderInterface* reader = currCase->summaryReader(); - - const std::vector& readerAddresses = reader->allResultAddresses(); - for (const auto& readerAddress : readerAddresses) + if (selectedAddressesFromUi.count(addressFromSource) > 0) { - if (selectedAddressesFromUi.count(readerAddress) > 0) - { - curveDefinitions.insert(RiaSummaryCurveDefinition(currCase, readerAddress, ensemble)); - } + curveDefinitions.insert(RiaSummaryCurveDefinition(caseFromSource, addressFromSource, ensemble)); } } } @@ -313,6 +313,7 @@ std::vector RiuSummaryCurveDefSelection::selection() { RimSummaryCaseCollection* ensemble = dynamic_cast(currSource); RimSummaryCase* sourceCase = dynamic_cast(currSource); + if (ensemble) { std::set addressUnion = ensemble->calculateUnionOfSummaryAddresses(); @@ -328,10 +329,10 @@ std::vector RiuSummaryCurveDefSelection::selection() { if (!(sourceCase && sourceCase->summaryReader())) continue; - const std::vector& readerAddresses = sourceCase->summaryReader()->allResultAddresses(); - for ( const auto& addr : readerAddresses) + const std::set& readerAddresses = sourceCase->summaryReader()->allResultAddresses(); + for ( const auto& addr : selectedAddressesFromUi) { - if (selectedAddressesFromUi.count(addr)) + if (readerAddresses.count(addr)) { curveDefSelection.push_back(RiaSummaryCurveDefinition(sourceCase, addr, nullptr)); } @@ -481,7 +482,7 @@ std::set RiuSummaryCurveDefSelection::findPossibleSumm { RimSummaryCase* calcSumCase = calculatedSummaryCase(); - const std::vector allAddresses = calcSumCase->summaryReader()->allResultAddresses(); + const std::set allAddresses = calcSumCase->summaryReader()->allResultAddresses(); for (const auto& adr : allAddresses) { addressSet.insert(adr); @@ -868,7 +869,7 @@ void RiuSummaryCurveDefSelection::defineUiOrdering(QString uiConfigName, caf::Pd //-------------------------------------------------------------------------------------------------- std::set RiuSummaryCurveDefSelection::findPossibleSummaryAddressesFromSelectedCases(const SummaryIdentifierAndField *identifierAndField) { - std::vector cases; + std::vector sources; for (const auto& source : m_selectedSources()) { RimSummaryCase* sumCase = dynamic_cast(source.p()); @@ -876,15 +877,14 @@ std::set RiuSummaryCurveDefSelection::findPossibleSumm if (sumCase) { - if(!isObservedData(sumCase)) cases.push_back(sumCase); + if(!isObservedData(sumCase)) sources.push_back(sumCase); } else if (ensemble) { - const auto& ensembleCases = ensemble->allSummaryCases(); - cases.insert(cases.end(), ensembleCases.begin(), ensembleCases.end()); + sources.push_back(ensemble); } } - return findPossibleSummaryAddresses(cases, identifierAndField); + return findPossibleSummaryAddresses(sources, identifierAndField); } //-------------------------------------------------------------------------------------------------- @@ -892,7 +892,7 @@ std::set RiuSummaryCurveDefSelection::findPossibleSumm //-------------------------------------------------------------------------------------------------- std::set RiuSummaryCurveDefSelection::findPossibleSummaryAddressesFromSelectedObservedData(const SummaryIdentifierAndField *identifierAndField) { - std::vector obsData; + std::vector obsData; for (const auto& source : m_selectedSources()) { RimSummaryCase* sumCase = dynamic_cast(source.p()); @@ -908,7 +908,7 @@ std::set RiuSummaryCurveDefSelection::findPossibleSumm //-------------------------------------------------------------------------------------------------- /// Returns the summary addresses that match the selected item type and input selections made in GUI //-------------------------------------------------------------------------------------------------- -std::set RiuSummaryCurveDefSelection::findPossibleSummaryAddresses(const std::vector &selectedCases, +std::set RiuSummaryCurveDefSelection::findPossibleSummaryAddresses(const std::vector &selectedSources, const SummaryIdentifierAndField *identifierAndField) { std::set addrUnion; @@ -920,32 +920,42 @@ std::set RiuSummaryCurveDefSelection::findPossibleSumm return addrUnion; } - for (RimSummaryCase* currCase : selectedCases) + for (SummarySource* currSource : selectedSources) { - RifSummaryReaderInterface* reader = nullptr; - if (currCase) reader = currCase->summaryReader(); - if (reader) + std::set allAddresses; + + RimSummaryCase* currCase = dynamic_cast(currSource); + RimSummaryCaseCollection* currEnsemble = dynamic_cast(currSource); + + if (currCase) { - const std::vector& allAddresses = reader->allResultAddresses(); - int addressCount = static_cast(allAddresses.size()); + RifSummaryReaderInterface* reader = nullptr; + if (currCase) reader = currCase->summaryReader(); + if (reader) allAddresses = reader->allResultAddresses(); + } + else if (currEnsemble) + { + allAddresses = currEnsemble->calculateUnionOfSummaryAddresses(); + } - bool applySelections = identifierAndField == nullptr || (!isVectorField && controllingIdentifierAndField != nullptr); - std::vector controllingFields; - if (applySelections) - { - // Build selections vector - controllingFields = buildControllingFieldList(identifierAndField); - } + int addressCount = static_cast(allAddresses.size()); - for (int i = 0; i < addressCount; i++) + bool applySelections = identifierAndField == nullptr || (!isVectorField && controllingIdentifierAndField != nullptr); + std::vector controllingFields; + if (applySelections) + { + // Build selections vector + controllingFields = buildControllingFieldList(identifierAndField); + } + + for(auto& address : allAddresses) + { + if (address.category() == m_currentSummaryCategory()) { - if (allAddresses[i].category() == m_currentSummaryCategory()) + bool addressSelected = applySelections ? isAddressCompatibleWithControllingFieldSelection(address, controllingFields) : true; + if (addressSelected) { - bool addressSelected = applySelections ? isAddressCompatibleWithControllingFieldSelection(allAddresses[i], controllingFields) : true; - if (addressSelected) - { - addrUnion.insert(allAddresses[i]); - } + addrUnion.insert(address); } } } diff --git a/ApplicationCode/UserInterface/RiuSummaryCurveDefSelection.h b/ApplicationCode/UserInterface/RiuSummaryCurveDefSelection.h index a2c537a333..ca6dc826f9 100644 --- a/ApplicationCode/UserInterface/RiuSummaryCurveDefSelection.h +++ b/ApplicationCode/UserInterface/RiuSummaryCurveDefSelection.h @@ -76,7 +76,7 @@ private: caf::PdmUiEditorAttribute* attribute) override; - std::set findPossibleSummaryAddresses(const std::vector &selectedCases, + std::set findPossibleSummaryAddresses(const std::vector &selectedSources, const SummaryIdentifierAndField *identifierAndField); std::set findPossibleSummaryAddressesFromSelectedCases(const SummaryIdentifierAndField *identifierAndField); std::set findPossibleSummaryAddressesFromSelectedObservedData(const SummaryIdentifierAndField *identifierAndField); diff --git a/doc/summary_cases_and_readers.plantuml b/doc/summary_cases_and_readers.plantuml index 6e805848fa..068bb7cfb5 100644 --- a/doc/summary_cases_and_readers.plantuml +++ b/doc/summary_cases_and_readers.plantuml @@ -1,7 +1,6 @@ @startuml left to right direction - RifSummaryReaderInterface <|-- RifCalculatedSummaryCurveReader RifSummaryReaderInterface <|-- RifColumnBasedUserData RifSummaryReaderInterface <|-- RifCsvUserData @@ -9,17 +8,24 @@ RifSummaryReaderInterface <|-- RifKeywordVectorUserData RifSummaryReaderInterface <|-- RifReaderEclipseSummary RifSummaryReaderInterface <|-- RifReaderObservedData RifSummaryReaderInterface <|-- RifEnsembleStatisticsReader +RifSummaryReaderInterface <|-- RifDerivedEnsembleReader RifColumnBasedUserData *-- "1" RifColumnBasedUserDataParser RifKeywordVectorUserData *-- "1" RifKeywordVectorUserDataParser RifReaderObservedData --> RifCsvUserDataParser -RimSummaryCaseCollection -> "*" RimSummaryCase +RimSummaryCaseCollection --> "*" RimSummaryCase + +RimSummaryCaseCollection <|-- RimDerivedEnsembleCaseCollection + +RimDerivedEnsembleCaseCollection --> "*" RimDerivedEnsembleCase RimSummaryCase <|-- RimCalculatedSummaryCase RimSummaryCase <|-- RimFileSummaryCase RimSummaryCase <|-- RimGridSummaryCase RimSummaryCase <|-- RimObservedData +RimSummaryCase <|-- RimEnsembleStatisticsCase +RimSummaryCase <|-- RimDerivedEnsembleCase RimObservedData <|-- RimObservedEclipseUserData RimObservedData <|-- RimCsvUserData @@ -27,6 +33,8 @@ RimObservedData <|-- RimSummaryObservedDataFile RimCalculatedSummaryCase --> RifCalculatedSummaryCurveReader +RimDerivedEnsembleCase --> RifDerivedEnsembleReader + RimSummaryObservedDataFile --> RifReaderObservedData RimObservedEclipseUserData *--> RifSummaryReaderInterface @@ -44,4 +52,7 @@ RimEnsembleCurveSet -> "1" RimSummaryCaseCollection RimEnsembleCurveSet --> "1" RimEnsembleStatisticsCase RimEnsembleStatisticsCase --> RifEnsembleStatisticsReader +RimSummaryCaseMainCollection -> "*" RimSummaryCase +RimSummaryCaseMainCollection -> "*" RimSummaryCaseCollection + @enduml