///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2019- Equinor ASA // // ResInsight is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RifReaderFmuRft.h" #include "RiaLogging.h" #include "RiaQDateTimeTools.h" #include "cafAssert.h" #include #include #include #include #include //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RifReaderFmuRft::Observation::Observation() : utmx( -std::numeric_limits::infinity() ) , utmy( -std::numeric_limits::infinity() ) , mdrkb( -std::numeric_limits::infinity() ) , tvdmsl( -std::numeric_limits::infinity() ) , pressure( -std::numeric_limits::infinity() ) , pressureError( -std::numeric_limits::infinity() ) , formation() { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RifReaderFmuRft::Observation::valid() const { return utmx != std::numeric_limits::infinity() && utmy != std::numeric_limits::infinity() && mdrkb != std::numeric_limits::infinity() && tvdmsl != std::numeric_limits::infinity() && pressure != std::numeric_limits::infinity() && pressureError != std::numeric_limits::infinity(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RifReaderFmuRft::WellObservationSet::WellObservationSet( const QDateTime& dateTime, int measurementIndex ) : dateTime( dateTime ) , measurementIndex( measurementIndex ) { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RifReaderFmuRft::RifReaderFmuRft( const QString& filePath ) : m_filePath( filePath ) { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QStringList RifReaderFmuRft::findSubDirectoriesWithFmuRftData( const QString& filePath ) { QStringList subDirsContainingFmuRftData; QFileInfo fileInfo( filePath ); if ( !( fileInfo.exists() && fileInfo.isDir() && fileInfo.isReadable() ) ) { return subDirsContainingFmuRftData; } if ( directoryContainsFmuRftData( filePath ) ) { subDirsContainingFmuRftData.push_back( filePath ); } QDir dir( filePath ); QStringList subDirs = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable, QDir::Name ); for ( QString subDir : subDirs ) { QString absDir = dir.absoluteFilePath( subDir ); subDirsContainingFmuRftData.append( findSubDirectoriesWithFmuRftData( absDir ) ); } return subDirsContainingFmuRftData; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RifReaderFmuRft::directoryContainsFmuRftData( const QString& filePath ) { QFileInfo baseFileInfo( filePath ); if ( !( baseFileInfo.exists() && baseFileInfo.isDir() && baseFileInfo.isReadable() ) ) { return false; } QDir dir( filePath ); if ( !dir.exists( RifReaderFmuRft::wellPathFileName() ) ) { return false; } QStringList obsFiles; obsFiles << "*.obs" << "*.txt"; QFileInfoList fileInfos = dir.entryInfoList( obsFiles, QDir::Files, QDir::Name ); std::map fileStemCounts; for ( QFileInfo fileInfo : fileInfos ) { // TODO: // Uses completeBaseName() to support wells with a dot in the name. // Not sure if this is necessary or desired fileStemCounts[fileInfo.completeBaseName()]++; if ( fileStemCounts[fileInfo.completeBaseName()] == 2 ) { // At least one matching obs and txt file. return true; } } return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RifReaderFmuRft::wellPathFileName() { return "well_date_rft.txt"; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RifReaderFmuRft::labels( const RifEclipseRftAddress& rftAddress ) { std::vector formationLabels; if ( m_allWellObservations.empty() ) { load(); } auto it = m_allWellObservations.find( rftAddress.wellName() ); if ( it != m_allWellObservations.end() ) { const std::vector& observations = it->second.observations; for ( const Observation& observation : observations ) { formationLabels.push_back( QString( "%1 - Pressure: %2 +/- %3" ).arg( observation.formation ).arg( observation.pressure ).arg( observation.pressureError ) ); } } return formationLabels; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RifReaderFmuRft::eclipseRftAddresses() { if ( m_allWellObservations.empty() ) { load(); } std::set allAddresses; for ( const WellObservationPair& wellObservationPair : m_allWellObservations ) { const QString& wellName = wellObservationPair.first; const QDateTime& dateTime = wellObservationPair.second.dateTime; const std::vector& observations = wellObservationPair.second.observations; for ( const Observation& observation : observations ) { if ( observation.valid() ) { RifEclipseRftAddress tvdAddress = RifEclipseRftAddress::createAddress( wellName, dateTime, RifEclipseRftAddress::RftWellLogChannelType::TVD ); RifEclipseRftAddress mdAddress = RifEclipseRftAddress::createAddress( wellName, dateTime, RifEclipseRftAddress::RftWellLogChannelType::MD ); RifEclipseRftAddress pressureAddress = RifEclipseRftAddress::createAddress( wellName, dateTime, RifEclipseRftAddress::RftWellLogChannelType::PRESSURE ); RifEclipseRftAddress pressureErrorAddress = RifEclipseRftAddress::createAddress( wellName, dateTime, RifEclipseRftAddress::RftWellLogChannelType::PRESSURE_ERROR ); allAddresses.insert( tvdAddress ); allAddresses.insert( mdAddress ); allAddresses.insert( pressureAddress ); allAddresses.insert( pressureErrorAddress ); } } } return allAddresses; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifReaderFmuRft::values( const RifEclipseRftAddress& rftAddress, std::vector* values ) { CAF_ASSERT( values ); if ( m_allWellObservations.empty() ) { load(); } auto it = m_allWellObservations.find( rftAddress.wellName() ); if ( it != m_allWellObservations.end() ) { const std::vector& observations = it->second.observations; values->clear(); values->reserve( observations.size() ); for ( const Observation& observation : observations ) { switch ( rftAddress.wellLogChannel() ) { case RifEclipseRftAddress::RftWellLogChannelType::TVD: values->push_back( observation.tvdmsl ); break; case RifEclipseRftAddress::RftWellLogChannelType::MD: values->push_back( observation.mdrkb ); break; case RifEclipseRftAddress::RftWellLogChannelType::PRESSURE: values->push_back( observation.pressure ); break; case RifEclipseRftAddress::RftWellLogChannelType::PRESSURE_ERROR: values->push_back( observation.pressureError ); break; default: CAF_ASSERT( false && "Wrong channel type sent to Fmu RFT reader" ); } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifReaderFmuRft::load() { QString errorMsg; QFileInfo fileInfo( m_filePath ); if ( !( fileInfo.exists() && fileInfo.isDir() && fileInfo.isReadable() ) ) { errorMsg = QString( "Directory '%1' does not exist or isn't readable" ).arg( m_filePath ); RiaLogging::error( errorMsg ); return; } QDir dir( m_filePath ); WellObservationMap wellObservations = loadWellDates( dir, &errorMsg ); WellObservationMap validObservations; if ( wellObservations.empty() ) { if ( errorMsg.isEmpty() ) { errorMsg = QString( "'%1' contains no valid FMU RFT data" ).arg( m_filePath ); } RiaLogging::error( errorMsg ); return; } for ( auto it = wellObservations.begin(); it != wellObservations.end(); ++it ) { const QString& wellName = it->first; WellObservationSet& wellObservationSet = it->second; QString txtFile = QString( "%1.txt" ).arg( wellName ); QString obsFile = QString( "%1.obs" ).arg( wellName ); if ( !readTxtFile( dir.absoluteFilePath( txtFile ), &errorMsg, &wellObservationSet ) ) { RiaLogging::warning( errorMsg ); continue; } if ( !readObsFile( dir.absoluteFilePath( obsFile ), &errorMsg, &wellObservationSet ) ) { RiaLogging::warning( errorMsg ); continue; } validObservations.insert( *it ); } m_allWellObservations.swap( validObservations ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RifReaderFmuRft::availableTimeSteps( const QString& wellName, const RifEclipseRftAddress::RftWellLogChannelType& wellLogChannelName ) { if ( wellLogChannelName == RifEclipseRftAddress::RftWellLogChannelType::TVD || wellLogChannelName == RifEclipseRftAddress::RftWellLogChannelType::MD || wellLogChannelName == RifEclipseRftAddress::RftWellLogChannelType::PRESSURE ) { return availableTimeSteps( wellName ); } return {}; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RifReaderFmuRft::availableTimeSteps( const QString& wellName ) { if ( m_allWellObservations.empty() ) { load(); } auto it = m_allWellObservations.find( wellName ); if ( it != m_allWellObservations.end() ) { return { it->second.dateTime }; } return {}; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RifReaderFmuRft::availableTimeSteps( const QString& wellName, const std::set& relevantChannels ) { if ( relevantChannels.count( RifEclipseRftAddress::RftWellLogChannelType::TVD ) || relevantChannels.count( RifEclipseRftAddress::RftWellLogChannelType::MD ) || relevantChannels.count( RifEclipseRftAddress::RftWellLogChannelType::PRESSURE ) ) { return availableTimeSteps( wellName ); } return {}; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RifReaderFmuRft::availableWellLogChannels( const QString& wellName ) { if ( m_allWellObservations.empty() ) { load(); } if ( !m_allWellObservations.empty() ) { return { RifEclipseRftAddress::RftWellLogChannelType::TVD, RifEclipseRftAddress::RftWellLogChannelType::MD, RifEclipseRftAddress::RftWellLogChannelType::PRESSURE }; } return {}; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RifReaderFmuRft::wellNames() { if ( m_allWellObservations.empty() ) { load(); } std::set wellNames; for ( auto it = m_allWellObservations.begin(); it != m_allWellObservations.end(); ++it ) { wellNames.insert( it->first ); } return wellNames; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RifReaderFmuRft::WellObservationMap RifReaderFmuRft::loadWellDates( QDir& dir, QString* errorMsg ) { CAF_ASSERT( errorMsg ); WellObservationMap validObservations; QFileInfo wellDateFileInfo( dir.absoluteFilePath( RifReaderFmuRft::wellPathFileName() ) ); if ( !( wellDateFileInfo.exists() && wellDateFileInfo.isFile() && wellDateFileInfo.isReadable() ) ) { *errorMsg = QString( "%1 cannot be found at '%s'" ).arg( RifReaderFmuRft::wellPathFileName() ).arg( m_filePath ); return WellObservationMap(); } { QFile wellDateFile( wellDateFileInfo.absoluteFilePath() ); if ( !wellDateFile.open( QIODevice::Text | QIODevice::ReadOnly ) ) { *errorMsg = QString( "Could not read '%1'" ).arg( wellDateFileInfo.absoluteFilePath() ); return WellObservationMap(); } QTextStream fileStream( &wellDateFile ); while ( !fileStream.atEnd() ) { QString line = fileStream.readLine(); line = line.simplified(); if ( line.isNull() || line.isEmpty() ) { continue; } QTextStream lineStream( &line ); QString wellName; int day, month, year, measurementIndex; lineStream >> wellName >> day >> month >> year >> measurementIndex; if ( lineStream.status() != QTextStream::Ok ) { *errorMsg = QString( "Failed to parse '%1'" ).arg( wellDateFileInfo.absoluteFilePath() ); return WellObservationMap(); } QDateTime dateTime = RiaQDateTimeTools::createDateTime( QDate( year, month, day ) ); dateTime.setTimeSpec( Qt::UTC ); WellObservationSet observationSet( dateTime, measurementIndex ); validObservations.insert( std::make_pair( wellName, observationSet ) ); } } return validObservations; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RifReaderFmuRft::readTxtFile( const QString& fileName, QString* errorMsg, WellObservationSet* wellObservationSet ) { CAF_ASSERT( wellObservationSet ); QFile file( fileName ); if ( !( file.open( QIODevice::Text | QIODevice::ReadOnly ) ) ) { *errorMsg = QString( "Could not open '%1'" ).arg( fileName ); return false; } QTextStream stream( &file ); while ( true ) { QString line = stream.readLine().trimmed(); if ( line.isNull() || line.isEmpty() ) { break; } else { QTextStream lineStream( &line ); double utmx, utmy, mdrkb, tvdmsl; QString formationName; lineStream >> utmx >> utmy >> mdrkb >> tvdmsl >> formationName; if ( lineStream.status() != QTextStream::Ok ) { *errorMsg = QString( "Failed to parse '%1'" ).arg( fileName ); return false; } Observation observation; observation.utmx = utmx; observation.utmy = utmy; observation.mdrkb = mdrkb; observation.tvdmsl = tvdmsl; observation.formation = formationName; wellObservationSet->observations.push_back( observation ); } } return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RifReaderFmuRft::readObsFile( const QString& fileName, QString* errorMsg, WellObservationSet* wellObservationSet ) { QFile file( fileName ); if ( !( file.open( QIODevice::Text | QIODevice::ReadOnly ) ) ) { *errorMsg = QString( "Could not open '%1'" ).arg( fileName ); return false; } size_t lineNumber = 0u; QTextStream stream( &file ); while ( true ) { QString line = stream.readLine().trimmed(); if ( line.isNull() || line.isEmpty() ) { break; } else if ( lineNumber >= wellObservationSet->observations.size() ) { *errorMsg = QString( "'%1' has more lines than corresponding txt file" ).arg( fileName ); return false; } else { QTextStream lineStream( &line ); double pressure, pressureError; lineStream >> pressure >> pressureError; if ( lineStream.status() != QTextStream::Ok ) { *errorMsg = QString( "Failed to parse line %1 of '%2'" ).arg( lineNumber + 1 ).arg( fileName ); return false; } Observation& observation = wellObservationSet->observations[lineNumber]; observation.pressure = pressure; observation.pressureError = pressureError; } lineNumber++; } if ( lineNumber != wellObservationSet->observations.size() ) { *errorMsg = QString( "'%1' has less lines than corresponding txt file" ).arg( fileName ); return false; } return true; }