From 4090e7050788f512a1762708cac1da282a560939 Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Tue, 14 Dec 2021 11:10:19 +0100 Subject: [PATCH] #8356 Summary Data : Add support for use of summary summary data from Python --- .../Application/RiaGuiApplication.cpp | 3 + .../FileInterface/CMakeLists_files.cmake | 4 + .../RifMultipleSummaryReaders.cpp | 121 ++++++++++ .../FileInterface/RifMultipleSummaryReaders.h | 47 ++++ .../RifProjectSummaryDataWriter.cpp | 220 ++++++++++++++++++ .../RifProjectSummaryDataWriter.h | 67 ++++++ .../Summary/RimFileSummaryCase.cpp | 200 +++++++++++++++- .../Summary/RimFileSummaryCase.h | 22 +- .../Summary/RimSummaryCaseMainCollection.cpp | 16 ++ .../Summary/RimSummaryCaseMainCollection.h | 1 + .../RimcSummaryCase.cpp | 56 +++++ .../RimcSummaryCase.h | 21 ++ .../rips/PythonExamples/summary_vectors.py | 5 + .../Python/rips/tests/test_summary_cases.py | 32 +++ 14 files changed, 809 insertions(+), 6 deletions(-) create mode 100644 ApplicationLibCode/FileInterface/RifMultipleSummaryReaders.cpp create mode 100644 ApplicationLibCode/FileInterface/RifMultipleSummaryReaders.h create mode 100644 ApplicationLibCode/FileInterface/RifProjectSummaryDataWriter.cpp create mode 100644 ApplicationLibCode/FileInterface/RifProjectSummaryDataWriter.h diff --git a/ApplicationLibCode/Application/RiaGuiApplication.cpp b/ApplicationLibCode/Application/RiaGuiApplication.cpp index f14eb7da0e..449a2578d3 100644 --- a/ApplicationLibCode/Application/RiaGuiApplication.cpp +++ b/ApplicationLibCode/Application/RiaGuiApplication.cpp @@ -32,6 +32,7 @@ #include "RiaProjectModifier.h" #include "RiaRegressionTestRunner.h" #include "RiaSocketServer.h" +#include "RiaSummaryTools.h" #include "RiaVersionInfo.h" #include "RiaViewRedrawScheduler.h" @@ -1338,6 +1339,8 @@ void RiaGuiApplication::onProjectBeingSaved() { setLastUsedDialogDirectory( "BINARY_GRID", QFileInfo( m_project->fileName() ).absolutePath() ); storeTreeViewState(); + + RiaSummaryTools::summaryCaseMainCollection()->onProjectBeingSaved(); } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/FileInterface/CMakeLists_files.cmake b/ApplicationLibCode/FileInterface/CMakeLists_files.cmake index a9f5eb43d4..a45e86fc81 100644 --- a/ApplicationLibCode/FileInterface/CMakeLists_files.cmake +++ b/ApplicationLibCode/FileInterface/CMakeLists_files.cmake @@ -69,6 +69,8 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RifWellIAFileWriter.h ${CMAKE_CURRENT_LIST_DIR}/RifEclipseTextFileReader.h ${CMAKE_CURRENT_LIST_DIR}/RifEclipseKeywordContent.h + ${CMAKE_CURRENT_LIST_DIR}/RifMultipleSummaryReaders.h + ${CMAKE_CURRENT_LIST_DIR}/RifProjectSummaryDataWriter.h ) set(SOURCE_GROUP_SOURCE_FILES @@ -139,6 +141,8 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RifEclEclipseSummary.cpp ${CMAKE_CURRENT_LIST_DIR}/RifWellIAFileWriter.cpp ${CMAKE_CURRENT_LIST_DIR}/RifEclipseTextFileReader.cpp + ${CMAKE_CURRENT_LIST_DIR}/RifMultipleSummaryReaders.cpp + ${CMAKE_CURRENT_LIST_DIR}/RifProjectSummaryDataWriter.cpp ) list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES}) diff --git a/ApplicationLibCode/FileInterface/RifMultipleSummaryReaders.cpp b/ApplicationLibCode/FileInterface/RifMultipleSummaryReaders.cpp new file mode 100644 index 0000000000..0f630033e6 --- /dev/null +++ b/ApplicationLibCode/FileInterface/RifMultipleSummaryReaders.cpp @@ -0,0 +1,121 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RifMultipleSummaryReaders.h" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifMultipleSummaryReaders::RifMultipleSummaryReaders() = default; + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RifMultipleSummaryReaders::addReader( RifSummaryReaderInterface* reader ) +{ + for ( auto existingReader : m_readers ) + { + if ( existingReader.p() == reader ) return; + } + + m_readers.push_back( reader ); + + rebuildMetaData(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RifMultipleSummaryReaders::removeReader( RifSummaryReaderInterface* reader ) +{ + m_readers.erase( reader ); + rebuildMetaData(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const std::vector& RifMultipleSummaryReaders::timeSteps( const RifEclipseSummaryAddress& resultAddress ) const +{ + for ( const auto& r : m_readers ) + { + if ( r->hasAddress( resultAddress ) ) return r->timeSteps( resultAddress ); + } + + static std::vector empty; + + return empty; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifMultipleSummaryReaders::values( const RifEclipseSummaryAddress& resultAddress, std::vector* values ) const +{ + for ( const auto& r : m_readers ) + { + if ( r->hasAddress( resultAddress ) ) return r->values( resultAddress, values ); + } + + return false; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::string RifMultipleSummaryReaders::unitName( const RifEclipseSummaryAddress& resultAddress ) const +{ + for ( const auto& r : m_readers ) + { + if ( r->hasAddress( resultAddress ) ) return r->unitName( resultAddress ); + } + + return {}; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RiaDefines::EclipseUnitSystem RifMultipleSummaryReaders::unitSystem() const +{ + CVF_ASSERT( !m_readers.empty() ); + + return m_readers.at( 0 )->unitSystem(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RifMultipleSummaryReaders::rebuildMetaData() +{ + m_allErrorAddresses.clear(); + m_allResultAddresses.clear(); + + for ( const auto& reader : m_readers ) + { + { + auto resultAddresses = reader->allResultAddresses(); + m_allResultAddresses.insert( resultAddresses.begin(), resultAddresses.end() ); + } + + { + auto errorResultAddresses = reader->allErrorAddresses(); + m_allErrorAddresses.insert( errorResultAddresses.begin(), errorResultAddresses.end() ); + } + } +} diff --git a/ApplicationLibCode/FileInterface/RifMultipleSummaryReaders.h b/ApplicationLibCode/FileInterface/RifMultipleSummaryReaders.h new file mode 100644 index 0000000000..4adddef029 --- /dev/null +++ b/ApplicationLibCode/FileInterface/RifMultipleSummaryReaders.h @@ -0,0 +1,47 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "RifSummaryReaderInterface.h" + +#include "cvfCollection.h" + +#include + +//================================================================================================== +/// +//================================================================================================== +class RifMultipleSummaryReaders : public RifSummaryReaderInterface +{ +public: + RifMultipleSummaryReaders(); + + void addReader( RifSummaryReaderInterface* reader ); + void removeReader( RifSummaryReaderInterface* reader ); + + const std::vector& timeSteps( const RifEclipseSummaryAddress& resultAddress ) const override; + bool values( const RifEclipseSummaryAddress& resultAddress, std::vector* values ) const override; + std::string unitName( const RifEclipseSummaryAddress& resultAddress ) const override; + RiaDefines::EclipseUnitSystem unitSystem() const override; + + void rebuildMetaData(); + +private: + cvf::Collection m_readers; +}; diff --git a/ApplicationLibCode/FileInterface/RifProjectSummaryDataWriter.cpp b/ApplicationLibCode/FileInterface/RifProjectSummaryDataWriter.cpp new file mode 100644 index 0000000000..967c78fca2 --- /dev/null +++ b/ApplicationLibCode/FileInterface/RifProjectSummaryDataWriter.cpp @@ -0,0 +1,220 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RifProjectSummaryDataWriter.h" + +#include "RifSummaryReaderInterface.h" + +#include "opm/common/utility/TimeService.hpp" +#include "opm/io/eclipse/EclOutput.hpp" +#include "opm/io/eclipse/ExtESmry.hpp" + +#include "cafAssert.h" + +#include + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifProjectSummaryDataWriter::RifProjectSummaryDataWriter() + : m_timeStepCount( 0 ) +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RifProjectSummaryDataWriter::importFromSourceSummaryReader( const RifSummaryReaderInterface* reader ) +{ + if ( !reader ) return; + + std::string keyword = "TIME"; + auto summaryAddress = RifEclipseSummaryAddress::miscAddress( keyword ); + if ( reader->hasAddress( summaryAddress ) ) + { + auto timeSteps = reader->timeSteps( summaryAddress ); + if ( !timeSteps.empty() ) + { + Opm::TimeStampUTC ts( timeSteps.front() ); + m_startTime = { ts.day(), ts.month(), ts.year(), ts.hour(), ts.minutes(), ts.seconds(), 0 }; + } + + std::vector values; + reader->values( summaryAddress, &values ); + + const auto& unitString = reader->unitName( summaryAddress ); + + m_keywords.push_back( keyword ); + m_units.push_back( unitString ); + + std::vector floatValues; + floatValues.reserve( values.size() ); + for ( const auto& v : values ) + { + floatValues.push_back( v ); + } + + m_values.push_back( floatValues ); + + m_timeStepCount = values.size(); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RifProjectSummaryDataWriter::importFromProjectSummaryFile( const std::string& projectSummaryFileName ) +{ + try + { + Opm::EclIO::ExtESmry sourceSummary( projectSummaryFileName ); + + Opm::TimeStampUTC ts( std::chrono::system_clock::to_time_t( sourceSummary.startdate() ) ); + m_startTime = { ts.day(), ts.month(), ts.year(), ts.hour(), ts.minutes(), ts.seconds(), 0 }; + + auto keywords = sourceSummary.keywordList(); + for ( const auto& keyword : keywords ) + { + const auto& values = sourceSummary.get( keyword ); + const auto& unitString = sourceSummary.get_unit( keyword ); + + m_keywords.push_back( keyword ); + m_units.push_back( unitString ); + m_values.push_back( values ); + + if ( m_timeStepCount == 0 ) + { + m_timeStepCount = values.size(); + } + } + } + catch ( ... ) + { + std::string txt = "Error detected during import of data from " + projectSummaryFileName; + m_errorMessages.push_back( txt ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RifProjectSummaryDataWriter::setData( const std::vector& keywords, + const std::vector& units, + const std::vector>& values ) +{ + if ( keywords.empty() ) return; + + CAF_ASSERT( keywords.size() == units.size() ); + CAF_ASSERT( keywords.size() == values.size() ); + + for ( size_t i = 0; i < keywords.size(); i++ ) + { + auto existingIndex = indexForKeyword( keywords[i] ); + if ( existingIndex == -1 ) + { + m_keywords.push_back( keywords[i] ); + m_units.push_back( units[i] ); + m_values.push_back( values[i] ); + } + else + { + // Overwrite existing data + + m_keywords[existingIndex] = keywords[i]; + m_units[existingIndex] = units[i]; + m_values[existingIndex] = values[i]; + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RifProjectSummaryDataWriter::writeDataToFile( const std::string& fileName ) +{ + // Reference to other locations writing to ESMRY files + // ESmry::make_esmry_file() + // ExtSmryOutput::write() + + try + { + // The ExtESmry reader supports only binary mode, set formatted to false + bool formatted = false; + Opm::EclIO::EclOutput outFile( fileName, formatted, std::ios::out ); + + outFile.write( "START", m_startTime ); + outFile.write( "KEYCHECK", m_keywords ); + outFile.write( "UNITS", m_units ); + + { + // Bool array 1 means RSTEP, 0 means no RSTEP + // Dummy values, but required by the reader + std::vector intValues( m_timeStepCount, 1 ); + outFile.write( "RSTEP", intValues ); + } + + { + // TSTEP represents time steps + // Dummy values, but required by the reader + std::vector intValues; + intValues.resize( m_timeStepCount ); + std::iota( intValues.begin(), intValues.end(), 0 ); + outFile.write( "TSTEP", intValues ); + } + + for ( size_t i = 0; i < static_cast( m_keywords.size() ); i++ ) + { + std::string vect_name = "V" + std::to_string( i ); + outFile.write( vect_name, m_values[i] ); + } + } + catch ( ... ) + { + std::string txt = "Error detected during export of data to " + fileName; + m_errorMessages.push_back( txt ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RifProjectSummaryDataWriter::errorMessages() const +{ + return m_errorMessages; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RifProjectSummaryDataWriter::clearErrorMessages() +{ + m_errorMessages.clear(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +int RifProjectSummaryDataWriter::indexForKeyword( const std::string& keyword ) const +{ + for ( int i = 0; i < static_cast( m_keywords.size() ); i++ ) + { + if ( m_keywords[i] == keyword ) return i; + } + + return -1; +} diff --git a/ApplicationLibCode/FileInterface/RifProjectSummaryDataWriter.h b/ApplicationLibCode/FileInterface/RifProjectSummaryDataWriter.h new file mode 100644 index 0000000000..c1ec6e3373 --- /dev/null +++ b/ApplicationLibCode/FileInterface/RifProjectSummaryDataWriter.h @@ -0,0 +1,67 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include + +class RifEclipseSummaryAddress; +class RifSummaryReaderInterface; + +//================================================================================================== +// +// +//================================================================================================== +class RifProjectSummaryDataWriter +{ +public: + RifProjectSummaryDataWriter(); + + // Import start time, time values, and time step count from a source summary case + void importFromSourceSummaryReader( const RifSummaryReaderInterface* reader ); + + // Import all data from project summary file. This file can than be overwritten using writeDataToFile() + void importFromProjectSummaryFile( const std::string& projectSummaryFileName ); + + // Set data for a list of keyword/unit/values. If a keyword exist, the data will be overwritten + void setData( const std::vector& keywords, + const std::vector& units, + const std::vector>& values ); + + void writeDataToFile( const std::string& fileName ); + + std::vector errorMessages() const; + void clearErrorMessages(); + +private: + int indexForKeyword( const std::string& keyword ) const; + +private: + // Structure used to represent start time defined in the following order + // [DAY, MONTH, YEAR, HOUR, MINUTE, SECOND, MILLI_SEC] + std::vector m_startTime; + + std::vector m_keywords; + std::vector m_units; + std::vector> m_values; + + size_t m_timeStepCount; + + std::vector m_errorMessages; +}; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimFileSummaryCase.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimFileSummaryCase.cpp index 8aa453d33b..97299d57fe 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimFileSummaryCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimFileSummaryCase.cpp @@ -18,21 +18,29 @@ #include "RimFileSummaryCase.h" +#include "RiaApplication.h" #include "RiaLogging.h" #include "RicfCommandObject.h" + #include "RifEclipseSummaryTools.h" +#include "RifMultipleSummaryReaders.h" +#include "RifOpmCommonSummary.h" +#include "RifProjectSummaryDataWriter.h" #include "RifReaderEclipseRft.h" #include "RifReaderEclipseSummary.h" #include "RifSummaryReaderMultipleFiles.h" +#include "RimProject.h" #include "RimTools.h" #include "cafPdmFieldScriptingCapability.h" #include "cafPdmObjectScriptingCapability.h" +#include "cafPdmUiFilePathEditor.h" #include #include +#include //================================================================================================== // @@ -48,8 +56,12 @@ RimFileSummaryCase::RimFileSummaryCase() { CAF_PDM_InitScriptableObject( "File Summary Case ", ":/SummaryCases16x16.png", "", "A Summary Case based on SMSPEC files" ); CAF_PDM_InitScriptableField( &m_includeRestartFiles, "IncludeRestartFiles", false, "Include Restart Files" ); - m_includeRestartFiles.uiCapability()->setUiHidden( true ); + + CAF_PDM_InitFieldNoDefault( &m_additionalSummaryFilePath, + "AdditionalSummaryFilePath", + "Additional File Path (set invisible when ready)" ); + m_additionalSummaryFilePath.uiCapability()->setUiHidden( true ); } //-------------------------------------------------------------------------------------------------- @@ -82,9 +94,14 @@ QString RimFileSummaryCase::caseName() const //-------------------------------------------------------------------------------------------------- void RimFileSummaryCase::createSummaryReaderInterfaceThreadSafe( RiaThreadSafeLogger* threadSafeLogger ) { - m_summaryFileReader = RimFileSummaryCase::findRelatedFilesAndCreateReader( this->summaryHeaderFilename(), + m_fileSummaryReader = RimFileSummaryCase::findRelatedFilesAndCreateReader( this->summaryHeaderFilename(), m_includeRestartFiles, threadSafeLogger ); + + m_multiSummaryReader = new RifMultipleSummaryReaders; + m_multiSummaryReader->addReader( m_fileSummaryReader.p() ); + + openAndAttachAdditionalReader(); } //-------------------------------------------------------------------------------------------------- @@ -93,9 +110,13 @@ void RimFileSummaryCase::createSummaryReaderInterfaceThreadSafe( RiaThreadSafeLo void RimFileSummaryCase::createSummaryReaderInterface() { RiaThreadSafeLogger threadSafeLogger; - m_summaryFileReader = RimFileSummaryCase::findRelatedFilesAndCreateReader( this->summaryHeaderFilename(), + m_fileSummaryReader = RimFileSummaryCase::findRelatedFilesAndCreateReader( this->summaryHeaderFilename(), m_includeRestartFiles, &threadSafeLogger ); + m_multiSummaryReader = new RifMultipleSummaryReaders; + m_multiSummaryReader->addReader( m_fileSummaryReader.p() ); + + openAndAttachAdditionalReader(); auto messages = threadSafeLogger.messages(); for ( const auto& m : messages ) @@ -181,16 +202,52 @@ RifReaderEclipseRft* RimFileSummaryCase::findRftDataAndCreateReader( const QStri return nullptr; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimFileSummaryCase::defineEditorAttribute( const caf::PdmFieldHandle* field, + QString uiConfigName, + caf::PdmUiEditorAttribute* attribute ) +{ + if ( field == &m_additionalSummaryFilePath ) + { + caf::PdmUiFilePathEditorAttribute* myAttr = dynamic_cast( attribute ); + if ( myAttr ) + { + myAttr->m_selectSaveFileName = true; + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimFileSummaryCase::openAndAttachAdditionalReader() +{ + QString additionalSummaryFilePath = m_additionalSummaryFilePath().path(); + + cvf::ref opmCommonReader = new RifOpmCommonEclipseSummary; + opmCommonReader->useEnhancedSummaryFiles( true ); + + bool includeRestartFiles = false; + auto isValid = opmCommonReader->open( additionalSummaryFilePath, includeRestartFiles, nullptr ); + if ( isValid ) + { + m_multiSummaryReader->addReader( opmCommonReader.p() ); + m_additionalSummaryFileReader = opmCommonReader; + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RifSummaryReaderInterface* RimFileSummaryCase::summaryReader() { - if ( m_summaryFileReader.isNull() ) + if ( m_multiSummaryReader.isNull() ) { createSummaryReaderInterface(); } - return m_summaryFileReader.p(); + return m_multiSummaryReader.p(); } //-------------------------------------------------------------------------------------------------- @@ -212,3 +269,136 @@ void RimFileSummaryCase::setIncludeRestartFiles( bool includeRestartFiles ) { m_includeRestartFiles = includeRestartFiles; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimFileSummaryCase::setSummaryData( const std::string& keyword, const std::string& unit, const std::vector& values ) +{ + size_t mainSummaryFileValueCount = m_fileSummaryReader->timeSteps( RifEclipseSummaryAddress() ).size(); + if ( values.size() != mainSummaryFileValueCount ) + { + QString txt = QString( "Wrong size of summary data for keyword %1. Expected %2 values, received %3 values" ) + .arg( QString::fromStdString( keyword ) ) + .arg( mainSummaryFileValueCount ) + .arg( values.size() ); + RiaLogging::error( txt ); + + return; + } + + // Remove existing reader to be able to write to the summary file + m_multiSummaryReader->removeReader( m_additionalSummaryFileReader.p() ); + m_additionalSummaryFileReader = nullptr; + + RifProjectSummaryDataWriter projectSummaryDataWriter; + + QString tmpAdditionalSummaryFilePath = m_additionalSummaryFilePath().path(); + QFileInfo fi( tmpAdditionalSummaryFilePath ); + if ( fi.exists() ) + { + projectSummaryDataWriter.importFromProjectSummaryFile( tmpAdditionalSummaryFilePath.toStdString() ); + } + else + { + projectSummaryDataWriter.importFromSourceSummaryReader( m_fileSummaryReader.p() ); + + auto tempFilePath = additionalSummaryDataFilePath(); + + m_additionalSummaryFilePath = tempFilePath; + tmpAdditionalSummaryFilePath = tempFilePath; + } + + projectSummaryDataWriter.setData( { keyword }, { unit }, { values } ); + + std::string outputFilePath = tmpAdditionalSummaryFilePath.toStdString(); + projectSummaryDataWriter.writeDataToFile( outputFilePath ); + + for ( const auto& txt : projectSummaryDataWriter.errorMessages() ) + { + RiaLogging::error( QString::fromStdString( txt ) ); + } + projectSummaryDataWriter.clearErrorMessages(); + + openAndAttachAdditionalReader(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimFileSummaryCase::onProjectBeingSaved() +{ + // If additional data is stored in temp folder, copy to project folder and remove file in temp folder + + auto existingFilePath = m_additionalSummaryFilePath().path(); + if ( QFile::exists( existingFilePath ) ) + { + auto currentFilePath = additionalSummaryDataFilePath(); + if ( existingFilePath != currentFilePath ) + { + if ( QFile::copy( existingFilePath, currentFilePath ) ) + { + QFile::remove( existingFilePath ); + m_additionalSummaryFilePath = currentFilePath; + + openAndAttachAdditionalReader(); + } + else + { + QString txt = "Error when copying temporary file to " + currentFilePath; + RiaLogging::error( txt ); + } + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimFileSummaryCase::createAdditionalSummaryFileName() +{ + QUuid uuid = QUuid::createUuid(); + QString uuidString = uuid.toString(); + uuidString.remove( '{' ).remove( '}' ); + + auto filePath = "RI_SUMMARY_DATA_" + uuidString + ".ESMRY"; + + return filePath; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimFileSummaryCase::additionalSummaryDataFilePath() const +{ + QDir storageDir; + RiaApplication* app = RiaApplication::instance(); + if ( app->isProjectSavedToDisc() ) + { + QString projectFileName = RimProject::current()->fileName(); + + QFileInfo fileInfo( projectFileName ); + storageDir = fileInfo.dir(); + } + else + { + // If project is not saved, use the temp folder + storageDir = QDir::temp(); + } + + QString fileName; + auto cacheSummaryFilePath = m_additionalSummaryFilePath().path(); + if ( cacheSummaryFilePath.isEmpty() ) + { + fileName = createAdditionalSummaryFileName(); + } + else + { + QFileInfo fi( cacheSummaryFilePath ); + fileName = fi.fileName(); + } + + auto filePath = storageDir.absoluteFilePath( fileName ); + + return filePath; +} diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimFileSummaryCase.h b/ApplicationLibCode/ProjectDataModel/Summary/RimFileSummaryCase.h index b5e719923d..7ae8e94092 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimFileSummaryCase.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimFileSummaryCase.h @@ -26,6 +26,9 @@ class RifReaderRftInterface; class RifReaderEclipseRft; class RifReaderEclipseSummary; class RiaThreadSafeLogger; +class RifOpmCommonEclipseSummary; +class RifEclipseSummaryAddress; +class RifMultipleSummaryReaders; //================================================================================================== // @@ -51,14 +54,31 @@ public: void setIncludeRestartFiles( bool includeRestartFiles ); + void setSummaryData( const std::string& keyword, const std::string& unit, const std::vector& values ); + void onProjectBeingSaved(); + static RifSummaryReaderInterface* findRelatedFilesAndCreateReader( const QString& headerFileName, bool includeRestartFiles, RiaThreadSafeLogger* threadSafeLogger ); static RifReaderEclipseRft* findRftDataAndCreateReader( const QString& headerFileName ); +protected: + void defineEditorAttribute( const caf::PdmFieldHandle* field, + QString uiConfigName, + caf::PdmUiEditorAttribute* attribute ) override; + private: - cvf::ref m_summaryFileReader; + void openAndAttachAdditionalReader(); + QString additionalSummaryDataFilePath() const; + static QString createAdditionalSummaryFileName(); + +private: + cvf::ref m_fileSummaryReader; + cvf::ref m_multiSummaryReader; cvf::ref m_summaryEclipseRftReader; caf::PdmField m_includeRestartFiles; + + caf::PdmField m_additionalSummaryFilePath; + cvf::ref m_additionalSummaryFileReader; }; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseMainCollection.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseMainCollection.cpp index 813bafd5b0..8a6aef6718 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseMainCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseMainCollection.cpp @@ -728,3 +728,19 @@ void RimSummaryCaseMainCollection::updateAutoShortName() s->updateAutoShortName(); } } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryCaseMainCollection::onProjectBeingSaved() +{ + auto sumCases = allSummaryCases(); + for ( auto s : sumCases ) + { + auto fileSumCase = dynamic_cast( s ); + if ( fileSumCase ) + { + fileSumCase->onProjectBeingSaved(); + } + } +} diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseMainCollection.h b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseMainCollection.h index e80617713f..786357b733 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseMainCollection.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCaseMainCollection.h @@ -71,6 +71,7 @@ public: QString uniqueShortNameForCase( RimSummaryCase* summaryCase ); void updateAutoShortName(); + void onProjectBeingSaved(); private: static void loadSummaryCaseData( std::vector summaryCases ); diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcSummaryCase.cpp b/ApplicationLibCode/ProjectDataModelCommands/RimcSummaryCase.cpp index 76dbd4322d..e1fda8b2f1 100644 --- a/ApplicationLibCode/ProjectDataModelCommands/RimcSummaryCase.cpp +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcSummaryCase.cpp @@ -22,6 +22,8 @@ #include "RiaSummaryTools.h" #include "RifSummaryReaderInterface.h" + +#include "RimFileSummaryCase.h" #include "RimSummaryCase.h" #include "RimcDataContainerDouble.h" @@ -257,3 +259,57 @@ std::unique_ptr RimSummaryCase_resampleValues::defaultResu { return std::unique_ptr( new RimcSummaryResampleData ); } + +CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimSummaryCase, RimSummaryCase_setSummaryVectorValues, "setSummaryValues" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimSummaryCase_setSummaryVectorValues::RimSummaryCase_setSummaryVectorValues( caf::PdmObjectHandle* self ) + : caf::PdmObjectMethod( self ) +{ + CAF_PDM_InitObject( "Set Summary Values" ); + + CAF_PDM_InitScriptableFieldNoDefault( &m_addressString, "Address", "", "", "", "Formatted address specifying the summary vector" ); + CAF_PDM_InitScriptableFieldNoDefault( &m_unitString, "Unit", "", "", "", "Unit" ); + CAF_PDM_InitScriptableFieldNoDefault( &m_values, "Values", "", "", "", "Values" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +caf::PdmObjectHandle* RimSummaryCase_setSummaryVectorValues::execute() +{ + auto* summaryCase = self(); + auto* fileSummaryCase = dynamic_cast( summaryCase ); + if ( fileSummaryCase ) + { + fileSummaryCase->setSummaryData( m_addressString().toStdString(), m_unitString().toStdString(), m_values() ); + } + + return nullptr; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimSummaryCase_setSummaryVectorValues::resultIsPersistent() const +{ + return false; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::unique_ptr RimSummaryCase_setSummaryVectorValues::defaultResult() const +{ + return nullptr; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimSummaryCase_setSummaryVectorValues::isNullptrValidResult() const +{ + return true; +} diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcSummaryCase.h b/ApplicationLibCode/ProjectDataModelCommands/RimcSummaryCase.h index 8109c920ee..335b4bbb6b 100644 --- a/ApplicationLibCode/ProjectDataModelCommands/RimcSummaryCase.h +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcSummaryCase.h @@ -92,3 +92,24 @@ private: caf::PdmField m_addressString; caf::PdmField m_resamplingPeriod; }; + +//================================================================================================== +/// +//================================================================================================== +class RimSummaryCase_setSummaryVectorValues : public caf::PdmObjectMethod +{ + CAF_PDM_HEADER_INIT; + +public: + RimSummaryCase_setSummaryVectorValues( caf::PdmObjectHandle* self ); + + caf::PdmObjectHandle* execute() override; + bool resultIsPersistent() const override; + std::unique_ptr defaultResult() const override; + bool isNullptrValidResult() const override; + +private: + caf::PdmField m_addressString; + caf::PdmField m_unitString; + caf::PdmField> m_values; +}; diff --git a/GrpcInterface/Python/rips/PythonExamples/summary_vectors.py b/GrpcInterface/Python/rips/PythonExamples/summary_vectors.py index b55e0ebc23..04165aca13 100644 --- a/GrpcInterface/Python/rips/PythonExamples/summary_vectors.py +++ b/GrpcInterface/Python/rips/PythonExamples/summary_vectors.py @@ -29,3 +29,8 @@ print("\nResampled data") for t, value in zip(summary_data_sampled.time_steps, summary_data_sampled.values): print(time.strftime("%a, %d %b %Y ", time.gmtime(t)) + " | " + str(value)) + +summary_case.set_summary_values("FOPT_M1", "myUnit", summary_data.values) +summary_case.set_summary_values("FOPT_M2", "myUnit", summary_data.values) +summary_case.set_summary_values("FOPT_M3", "myUnit", summary_data.values) +summary_case.set_summary_values("FOPT_M4", "myUnit", summary_data.values) diff --git a/GrpcInterface/Python/rips/tests/test_summary_cases.py b/GrpcInterface/Python/rips/tests/test_summary_cases.py index 38e31e78d3..2537db245f 100644 --- a/GrpcInterface/Python/rips/tests/test_summary_cases.py +++ b/GrpcInterface/Python/rips/tests/test_summary_cases.py @@ -108,3 +108,35 @@ def test_summary_no_unsmry(rips_instance, initialize_test): summary_case = rips_instance.project.import_summary_case(temp_path) assert summary_case is None + + +def test_summary_set_values(rips_instance, initialize_test): + casePath = dataroot.PATH + "/flow_diagnostics_test/SIMPLE_SUMMARY2.SMSPEC" + summary_case = rips_instance.project.import_summary_case(casePath) + assert summary_case.id == 1 + + addresses = summary_case.available_addresses() + original_keyword_count = len(addresses.values) + + summary_data = summary_case.summary_vector_values("FOPT") + assert len(summary_data.values) == 60 + + summary_case.set_summary_values("FOPT_1", "", summary_data.values) + generated_summary_data = summary_case.summary_vector_values("FOPT_1") + assert len(generated_summary_data.values) == 60 + + addresses = summary_case.available_addresses() + current_keyword_count = len(addresses.values) + assert current_keyword_count == original_keyword_count + 1 + + # Using existing keyword will overwrite existing data + summary_case.set_summary_values("FOPT_1", "", summary_data.values) + addresses = summary_case.available_addresses() + current_keyword_count = len(addresses.values) + assert current_keyword_count == original_keyword_count + 1 + + # invalid value count, check that available addresses are unchanged + summary_case.set_summary_values("FOPT_2", "", []) + addresses = summary_case.available_addresses() + current_keyword_count = len(addresses.values) + assert current_keyword_count == original_keyword_count + 1