ResInsight/ApplicationLibCode/ProjectDataModel/Summary/RimFileSummaryCase.cpp
Magne Sjaastad 09c04cbcd0
Improve RFT reader performance
* #9963 Rft reader: Search for DATA file when required
WSEGLINK is used to establish the branch topology for MSW wells. Search and read WSEGLINK data when segment data is requested by the user. This can be a time consuming operation.
Avoid doing this search for standard RFT plots.

* Make sure that single summary curves are displayed correctly
* Make sure single summary curves are visible in the plot
2023-03-14 13:14:40 +01:00

480 lines
18 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// 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 <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#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 "RifReaderOpmRft.h"
#include "RifSummaryReaderMultipleFiles.h"
#include "RimCalculatedSummaryCurveReader.h"
#include "RimProject.h"
#include "RimRftCase.h"
#include "RimSummaryCalculationCollection.h"
#include "RimTools.h"
#include "cafPdmFieldScriptingCapability.h"
#include "cafPdmObjectScriptingCapability.h"
#include "cafPdmUiFilePathEditor.h"
#include "cafPdmUiTreeOrdering.h"
#include <QDir>
#include <QFileInfo>
#include <QUuid>
//==================================================================================================
//
//
//
//==================================================================================================
CAF_PDM_SOURCE_INIT( RimFileSummaryCase, "FileSummaryCase" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
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 );
CAF_PDM_InitFieldNoDefault( &m_rftCase, "RftCase", "RFT Data" );
m_rftCase = new RimRftCase;
m_hasParsedForWeseglink = false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimFileSummaryCase::~RimFileSummaryCase()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimFileSummaryCase::summaryHeaderFilename() const
{
return m_summaryHeaderFilename().path();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimFileSummaryCase::caseName() const
{
QFileInfo caseFileName( this->summaryHeaderFilename() );
return caseFileName.completeBaseName();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFileSummaryCase::createSummaryReaderInterfaceThreadSafe( RiaThreadSafeLogger* threadSafeLogger )
{
m_fileSummaryReader =
RimFileSummaryCase::findRelatedFilesAndCreateReader( this->summaryHeaderFilename(), m_includeRestartFiles, threadSafeLogger );
m_multiSummaryReader = new RifMultipleSummaryReaders;
m_multiSummaryReader->addReader( m_fileSummaryReader.p() );
openAndAttachAdditionalReader();
RimSummaryCalculationCollection* calcColl = RimProject::current()->calculationCollection();
m_calculatedSummaryReader = new RifCalculatedSummaryCurveReader( calcColl, this );
m_multiSummaryReader->addReader( m_calculatedSummaryReader.p() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFileSummaryCase::createSummaryReaderInterface()
{
RiaThreadSafeLogger threadSafeLogger;
createSummaryReaderInterfaceThreadSafe( &threadSafeLogger );
auto messages = threadSafeLogger.messages();
for ( const auto& m : messages )
{
RiaLogging::info( m );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFileSummaryCase::createRftReaderInterface()
{
QFileInfo fileInfo( summaryHeaderFilename() );
QString folder = fileInfo.absolutePath();
QString rftFileName = folder + "/" + fileInfo.completeBaseName() + ".RFT";
{
QFileInfo fi( rftFileName );
if ( fi.exists() )
{
m_rftCase()->setRftFileName( rftFileName );
}
}
// Usually, the data deck file path is empty at this point. If the user has specified the path manually, we use that.
// The intention is to use searchForWseglinkAndRecreateRftReader() to search for the data deck file path. Here we avoid searching for
// the data deck file by default, as this is a time consuming operation.
m_summaryEclipseRftReader = RimFileSummaryCase::createOpmRftReader( rftFileName, m_rftCase->dataDeckFilePath() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RifSummaryReaderInterface* RimFileSummaryCase::findRelatedFilesAndCreateReader( const QString& headerFileName,
bool includeRestartFiles,
RiaThreadSafeLogger* threadSafeLogger )
{
if ( includeRestartFiles )
{
std::vector<QString> warnings;
std::vector<RifRestartFileInfo> restartFileInfos = RifEclipseSummaryTools::getRestartFiles( headerFileName, warnings );
if ( !restartFileInfos.empty() )
{
std::vector<std::string> summaryFileNames;
summaryFileNames.push_back( headerFileName.toStdString() );
for ( const auto& s : restartFileInfos )
{
summaryFileNames.push_back( s.fileName.toStdString() );
}
// The ordering in intended to be start of history first, so we reverse the ordering
std::reverse( summaryFileNames.begin(), summaryFileNames.end() );
auto summaryReader = new RifSummaryReaderMultipleFiles( summaryFileNames );
if ( !summaryReader->createReadersAndImportMetaData( threadSafeLogger ) )
{
delete summaryReader;
return nullptr;
}
return summaryReader;
}
}
RifReaderEclipseSummary* summaryFileReader = new RifReaderEclipseSummary;
// All restart data is taken care of by RifSummaryReaderMultipleFiles, never read restart data from native file
// readers
if ( !summaryFileReader->open( headerFileName, threadSafeLogger ) )
{
delete summaryFileReader;
return nullptr;
}
return summaryFileReader;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RifReaderOpmRft* RimFileSummaryCase::createOpmRftReader( const QString& rftFileName, const QString& dataDeckFileName )
{
QFileInfo fi( rftFileName );
if ( fi.exists() )
{
return new RifReaderOpmRft( rftFileName, dataDeckFileName );
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFileSummaryCase::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
{
if ( field == &m_additionalSummaryFilePath )
{
auto* myAttr = dynamic_cast<caf::PdmUiFilePathEditorAttribute*>( attribute );
if ( myAttr )
{
myAttr->m_selectSaveFileName = true;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFileSummaryCase::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName /*= "" */ )
{
RimSummaryCase::defineUiTreeOrdering( uiTreeOrdering, uiConfigName );
if ( rftReader() )
{
uiTreeOrdering.add( m_rftCase() );
}
uiTreeOrdering.skipRemainingChildren();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFileSummaryCase::openAndAttachAdditionalReader()
{
QString additionalSummaryFilePath = m_additionalSummaryFilePath().path();
if ( additionalSummaryFilePath.isEmpty() ) return;
cvf::ref<RifOpmCommonEclipseSummary> 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_multiSummaryReader.isNull() )
{
createSummaryReaderInterface();
}
return m_multiSummaryReader.p();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RifReaderRftInterface* RimFileSummaryCase::rftReader()
{
if ( m_summaryEclipseRftReader.isNull() )
{
createRftReaderInterface();
}
return m_summaryEclipseRftReader.p();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFileSummaryCase::setIncludeRestartFiles( bool includeRestartFiles )
{
m_includeRestartFiles = includeRestartFiles;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFileSummaryCase::setSummaryData( const std::string& keyword, const std::string& unit, const std::vector<float>& 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;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFileSummaryCase::searchForWseglinkAndRecreateRftReader()
{
if ( m_hasParsedForWeseglink ) return;
if ( m_rftCase->dataDeckFilePath().isEmpty() )
{
QFileInfo fileInfo( summaryHeaderFilename() );
QString folder = fileInfo.absolutePath();
// Search for *.DATA file in same folder as summary file. If not found, search for a schedule file.
QString validDataDeckFileName;
QString dataDeckFileName = folder + "/" + fileInfo.completeBaseName() + ".DATA";
QFileInfo fi( dataDeckFileName );
if ( fi.exists() )
{
validDataDeckFileName = dataDeckFileName;
}
else
{
QString scheduleFileName = folder + "/" + fileInfo.completeBaseName() + ".SCH";
QFileInfo fi( scheduleFileName );
if ( fi.exists() )
{
validDataDeckFileName = scheduleFileName;
}
}
if ( !validDataDeckFileName.isEmpty() )
{
m_rftCase->setDataDeckFileName( dataDeckFileName );
// Create a new reader including the file path to the data deck file
// NB! This can be a time consuming operation
m_summaryEclipseRftReader = RimFileSummaryCase::createOpmRftReader( m_rftCase->rftFilePath(), m_rftCase->dataDeckFilePath() );
}
}
m_hasParsedForWeseglink = true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
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;
}