Files
ResInsight/ApplicationLibCode/FileInterface/RifEclipseOutputFileTools.cpp
Magne Sjaastad 56e98ba1c4 #14476 ApplicationLibCode: Replace CVF_ASSERT with CAF_ASSERT
Migrate all assert macros in ApplicationLibCode to CAF_ASSERT and remove every
use of cvfAssert.h.

CVF_ASSERT is replaced one to one. CVF_TIGHT_ASSERT is also replaced by
CAF_ASSERT, which is semantically exact: CVF_ENABLE_TIGHT_ASSERTS is 1 only
under _DEBUG, and that is what CAF_ASSERT now does. The two CVF_FAIL_MSG sites
become CAF_ASSERT( false && "message" ), preserving the message with the idiom
already used elsewhere in the code base.

Counts before and after: CVF_ASSERT 1044 to 0, CVF_TIGHT_ASSERT 66 to 0,
CVF_FAIL_MSG 2 to 0, cvfAssert.h references 154 to 0.

Include handling: files that included cvfAssert.h directly now include
cafAssert.h instead, includes left dead by the migration are removed, and files
that were relying on cvfAssert.h transitively get an explicit cafAssert.h. Files
that reach cafAssert.h through another caf header are left unchanged; a missing
include here is a compile error, not a silently disabled assert.

ResultStatisticsCache links only LibCore and therefore had no path to
cafAssert.h. Add the cafPdmCore directory as a private include path rather than
linking the library, since cafAssert.h is header only.

Note that this stops these asserts from firing in Release and RelWithDebInfo,
where CVF_ASSERT was previously active.
2026-08-07 15:05:57 +02:00

1078 lines
46 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2011- Statoil ASA
// Copyright (C) 2013- Ceetron Solutions AS
// Copyright (C) 2011-2012 Ceetron AS
//
// 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 "RifEclipseOutputFileTools.h"
#include "RiaLogging.h"
#include "RiaOpmParserTools.h"
#include "RiaQDateTimeTools.h"
#include "RiaStringEncodingTools.h"
#include "RifEclipseReportKeywords.h"
#include "RifEclipseRestartFilesetAccess.h"
#include "RifEclipseUnifiedRestartFileAccess.h"
#include "RigActiveCellInfo.h"
#include "RigCaseCellResultsData.h"
#include "RigEclipseCaseData.h"
#include "RigEclipseResultAddress.h"
#include "RigEclipseResultInfo.h"
#include "RigMainGrid.h"
#include "ert/ecl/ecl_file.h"
#include "ert/ecl/ecl_grid.h"
#include "ert/ecl/ecl_kw_magic.h"
#include "ert/ecl/ecl_nnc_data.h"
#include "ert/ecl/ecl_nnc_geometry.h"
#include "cafAssert.h"
#include "cafProgressInfo.h"
#include "cvfMath.h"
#include <QCryptographicHash>
#include <QFileInfo>
#include <algorithm>
#include <cassert>
//--------------------------------------------------------------------------------------------------
/// Constructor
//--------------------------------------------------------------------------------------------------
RifEclipseOutputFileTools::RifEclipseOutputFileTools()
{
}
//--------------------------------------------------------------------------------------------------
/// Destructor
//--------------------------------------------------------------------------------------------------
RifEclipseOutputFileTools::~RifEclipseOutputFileTools()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RifEclipseKeywordValueCount> RifEclipseOutputFileTools::keywordValueCounts( const std::vector<ecl_file_type*>& ecl_files )
{
auto reportstepMetaData = RifEclipseOutputFileTools::createReportStepsMetaData( ecl_files );
return reportstepMetaData.keywordValueCounts();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RifEclipseOutputFileTools::createResultEntries( const std::vector<RifEclipseKeywordValueCount>& fileKeywordInfo,
const std::vector<RigEclipseTimeStepInfo>& timeStepInfo,
RiaDefines::ResultCatType resultCategory,
RigEclipseCaseData* eclipseCaseData,
size_t totalTimeSteps )
{
if ( !eclipseCaseData ) return;
RigCaseCellResultsData* matrixModelResults = eclipseCaseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
RigCaseCellResultsData* fractureModelResults = eclipseCaseData->results( RiaDefines::PorosityModelType::FRACTURE_MODEL );
{
auto validKeywords = validKeywordsForPorosityModel( fileKeywordInfo,
eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ),
eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL ),
RiaDefines::PorosityModelType::MATRIX_MODEL,
totalTimeSteps );
if ( resultCategory == RiaDefines::ResultCatType::STATIC_NATIVE )
{
validKeywords.push_back( RifEclipseKeywordValueCount( "ACTNUM", 0, RifEclipseKeywordValueCount::KeywordDataType::INTEGER ) );
}
for ( const auto& keywordData : validKeywords )
{
RigEclipseResultAddress resAddr( resultCategory,
RifEclipseKeywordValueCount::mapType( keywordData.dataType() ),
QString::fromStdString( keywordData.keyword() ) );
matrixModelResults->createResultEntry( resAddr, false );
matrixModelResults->setTimeStepInfos( resAddr, timeStepInfo );
}
}
{
auto validKeywords = validKeywordsForPorosityModel( fileKeywordInfo,
eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ),
eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL ),
RiaDefines::PorosityModelType::FRACTURE_MODEL,
totalTimeSteps );
if ( resultCategory == RiaDefines::ResultCatType::STATIC_NATIVE )
{
validKeywords.push_back( RifEclipseKeywordValueCount( "ACTNUM", 0, RifEclipseKeywordValueCount::KeywordDataType::INTEGER ) );
}
for ( const auto& keywordData : validKeywords )
{
RigEclipseResultAddress resAddr( resultCategory,
RifEclipseKeywordValueCount::mapType( keywordData.dataType() ),
QString::fromStdString( keywordData.keyword() ) );
fractureModelResults->createResultEntry( resAddr, false );
fractureModelResults->setTimeStepInfos( resAddr, timeStepInfo );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RifEclipseOutputFileTools::getDayMonthYear( const ecl_kw_type* intehead_kw, int* day, int* month, int* year )
{
assert( day && month && year );
*day = ecl_kw_iget_int( intehead_kw, INTEHEAD_DAY_INDEX );
*month = ecl_kw_iget_int( intehead_kw, INTEHEAD_MONTH_INDEX );
*year = ecl_kw_iget_int( intehead_kw, INTEHEAD_YEAR_INDEX );
}
//--------------------------------------------------------------------------------------------------
/// Get list of time step texts (dates)
//--------------------------------------------------------------------------------------------------
void RifEclipseOutputFileTools::timeSteps( const ecl_file_type* ecl_file,
std::vector<QDateTime>* timeSteps,
std::vector<double>* daysSinceSimulationStart,
size_t* perTimeStepHeaderKeywordCount )
{
if ( !ecl_file ) return;
CAF_ASSERT( timeSteps && daysSinceSimulationStart );
timeSteps->clear();
daysSinceSimulationStart->clear();
// Get the number of occurrences of the INTEHEAD keyword
int numINTEHEAD = ecl_file_get_num_named_kw( ecl_file, INTEHEAD_KW );
// Get the number of occurrences of the DOUBHEAD keyword
int numDOUBHEAD = ecl_file_get_num_named_kw( ecl_file, DOUBHEAD_KW );
std::vector<double> dayValues( numINTEHEAD, 0.0 ); // Init fraction to zero
// Read out fraction of day if number of keywords are identical
if ( numINTEHEAD == numDOUBHEAD )
{
for ( int i = 0; i < numDOUBHEAD; i++ )
{
ecl_kw_type* kwDOUBHEAD = ecl_file_iget_named_kw( ecl_file, DOUBHEAD_KW, i );
if ( kwDOUBHEAD )
{
dayValues[i] = ecl_kw_iget_double( kwDOUBHEAD, DOUBHEAD_DAYS_INDEX );
}
}
}
bool useStartOfSimulationDate = true;
{
// See https://github.com/OPM/ResInsight/issues/4770
std::set<std::tuple<int, int, int>> uniqueDays;
for ( int i = 0; i < numINTEHEAD; i++ )
{
ecl_kw_type* kwINTEHEAD = ecl_file_iget_named_kw( ecl_file, INTEHEAD_KW, i );
CAF_ASSERT( kwINTEHEAD );
int day = 0;
int month = 0;
int year = 0;
getDayMonthYear( kwINTEHEAD, &day, &month, &year );
uniqueDays.insert( std::make_tuple( day, month, year ) );
}
std::set<double> uniqueDayValues;
for ( auto dayValue : dayValues )
{
uniqueDayValues.insert( dayValue );
}
if ( uniqueDays.size() == 1 && uniqueDayValues.size() == 1 )
{
int day = 0;
int month = 0;
int year = 0;
std::tie( day, month, year ) = *( uniqueDays.begin() );
QDateTime reportDateTime = RiaQDateTimeTools::createUtcDateTime( QDate( year, month, day ) );
timeSteps->push_back( reportDateTime );
daysSinceSimulationStart->push_back( *uniqueDayValues.begin() );
// Early return, we have only one unique time step
// This state has been seen for cases with one file for each time step
// See https://github.com/OPM/ResInsight/issues/4904
return;
}
// Some simulations might end up with wrong data reported for day/month/year. If this situation is detected,
// base all time step on double values and use start of simulation as first date
// See https://github.com/OPM/ResInsight/issues/4850
// https://github.com/OPM/ResInsight/issues/8558
if ( uniqueDays.size() == uniqueDayValues.size() ) useStartOfSimulationDate = false;
if ( uniqueDays.size() == dayValues.size() ) useStartOfSimulationDate = false;
}
std::set<QDateTime> existingTimesteps;
for ( int i = 0; i < numINTEHEAD; i++ )
{
ecl_kw_type* kwINTEHEAD = nullptr;
if ( useStartOfSimulationDate )
{
kwINTEHEAD = ecl_file_iget_named_kw( ecl_file, INTEHEAD_KW, 0 );
}
else
{
kwINTEHEAD = ecl_file_iget_named_kw( ecl_file, INTEHEAD_KW, i );
}
CAF_ASSERT( kwINTEHEAD );
int day = 0;
int month = 0;
int year = 0;
getDayMonthYear( kwINTEHEAD, &day, &month, &year );
QDateTime reportDateTime = RiaQDateTimeTools::createUtcDateTime( QDate( year, month, day ) );
CAF_ASSERT( reportDateTime.isValid() );
double dayDoubleValue = dayValues[i];
int dayValue = cvf::Math::floor( dayDoubleValue );
if ( useStartOfSimulationDate )
{
// Do not assume the first day value is zero. Adjust the day value to be relative to the first day value
// https://github.com/OPM/ResInsight/issues/11867
const int adjustedDayValue = dayValue - dayValues.front();
reportDateTime = reportDateTime.addDays( adjustedDayValue );
}
double dayFraction = dayDoubleValue - dayValue;
double milliseconds = dayFraction * 24.0 * 60.0 * 60.0 * 1000.0;
reportDateTime = reportDateTime.addMSecs( milliseconds );
if ( existingTimesteps.insert( reportDateTime ).second )
{
timeSteps->push_back( reportDateTime );
daysSinceSimulationStart->push_back( dayDoubleValue );
}
}
if ( perTimeStepHeaderKeywordCount )
{
// 6X simulator can report more then one keyword block per time step. Report the total number of keywords per
// time steps to be able to read data from correct index
//
// See https://github.com/OPM/ResInsight/issues/5763
//
*perTimeStepHeaderKeywordCount = numINTEHEAD / timeSteps->size();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RifEclipseOutputFileTools::keywordData( const ecl_file_type* ecl_file,
const QString& keyword,
size_t fileKeywordOccurrence,
std::vector<double>* values )
{
bool result = false;
#pragma omp critical( critical_section_keywordData_double )
{
auto nativeKeyword = RiaStringEncodingTools::toNativeEncoded( keyword );
// Guard against out-of-range occurrence. ecl_file_iget_named_kw() performs an unchecked vector access and
// crashes (SIGSEGV) if the requested occurrence does not exist in the active file view.
ecl_kw_type* kwData = nullptr;
if ( static_cast<int>( fileKeywordOccurrence ) < ecl_file_get_num_named_kw( ecl_file, nativeKeyword.data() ) )
{
kwData = ecl_file_iget_named_kw( ecl_file, nativeKeyword.data(), static_cast<int>( fileKeywordOccurrence ) );
}
if ( kwData )
{
size_t numValues = ecl_kw_get_size( kwData );
std::vector<double> doubleData;
doubleData.resize( numValues );
ecl_kw_get_data_as_double( kwData, doubleData.data() );
values->insert( values->end(), doubleData.begin(), doubleData.end() );
result = true;
}
}
return result;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RifEclipseOutputFileTools::keywordData( const ecl_file_type* ecl_file,
const QString& keyword,
size_t fileKeywordOccurrence,
std::vector<int>* values )
{
bool result = false;
#pragma omp critical( critical_section_keywordData_int )
{
auto nativeKeyword = RiaStringEncodingTools::toNativeEncoded( keyword );
// Guard against out-of-range occurrence. ecl_file_iget_named_kw() performs an unchecked vector access and
// crashes (SIGSEGV) if the requested occurrence does not exist in the active file view.
ecl_kw_type* kwData = nullptr;
if ( static_cast<int>( fileKeywordOccurrence ) < ecl_file_get_num_named_kw( ecl_file, nativeKeyword.data() ) )
{
kwData = ecl_file_iget_named_kw( ecl_file, nativeKeyword.data(), static_cast<int>( fileKeywordOccurrence ) );
}
if ( kwData )
{
size_t numValues = ecl_kw_get_size( kwData );
std::vector<int> integerData;
integerData.resize( numValues );
ecl_kw_get_memcpy_int_data( kwData, integerData.data() );
values->insert( values->end(), integerData.begin(), integerData.end() );
result = true;
}
}
return result;
}
//--------------------------------------------------------------------------------------------------
/// Get first occurrence of file of given type in given list of filenames, as filename or nullptr if not found
//--------------------------------------------------------------------------------------------------
QString RifEclipseOutputFileTools::firstFileNameOfType( const QStringList& fileSet, ecl_file_enum fileType )
{
int i;
for ( i = 0; i < fileSet.count(); i++ )
{
bool formatted = false;
int reportNumber = -1;
if ( ecl_util_get_file_type( RiaStringEncodingTools::toNativeEncoded( fileSet.at( i ) ).data(), &formatted, &reportNumber ) == fileType )
{
return fileSet.at( i );
}
}
return QString();
}
//--------------------------------------------------------------------------------------------------
/// Get all files of the given type from the provided list of filenames
//--------------------------------------------------------------------------------------------------
QStringList RifEclipseOutputFileTools::filterFileNamesOfType( const QStringList& fileSet, ecl_file_enum fileType )
{
QStringList fileNames;
int i;
for ( i = 0; i < fileSet.count(); i++ )
{
bool formatted = false;
int reportNumber = -1;
if ( ecl_util_get_file_type( RiaStringEncodingTools::toNativeEncoded( fileSet.at( i ) ).data(), &formatted, &reportNumber ) == fileType )
{
fileNames.append( fileSet.at( i ) );
}
}
return fileNames;
}
//-------------------------------------------------------------------------------------------------------
/// Check if resdata accepts the file name. resdata refuses to open files with mixed case in the file name.
//-------------------------------------------------------------------------------------------------------
bool RifEclipseOutputFileTools::isValidEclipseFileName( const QString& fileName )
{
QString fileNameBase = QFileInfo( fileName ).completeBaseName();
return ecl_util_valid_basename( RiaStringEncodingTools::toNativeEncoded( fileNameBase ).data() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QByteArray RifEclipseOutputFileTools::md5sum( const QString& fileName )
{
QFile file( fileName );
if ( file.open( QFile::ReadOnly ) )
{
QCryptographicHash hash( QCryptographicHash::Md5 );
QByteArray fileContent = file.readAll();
if ( !fileContent.isEmpty() )
{
hash.addData( fileContent );
return hash.result();
}
}
return QByteArray();
}
//--------------------------------------------------------------------------------------------------
/// Get set of Eclipse files based on an input file and its path
//--------------------------------------------------------------------------------------------------
bool RifEclipseOutputFileTools::findSiblingFilesWithSameBaseName( const QString& fullPathFileName, QStringList* baseNameFiles )
{
CAF_ASSERT( baseNameFiles );
baseNameFiles->clear();
QString filePath = QFileInfo( fullPathFileName ).absoluteFilePath();
filePath = QFileInfo( filePath ).path();
QString fileNameBase = QFileInfo( fullPathFileName ).completeBaseName();
stringlist_type* eclipseFiles = stringlist_alloc_new();
ecl_util_select_filelist( RiaStringEncodingTools::toNativeEncoded( filePath ).data(),
RiaStringEncodingTools::toNativeEncoded( fileNameBase ).data(),
ECL_OTHER_FILE,
false,
eclipseFiles );
int i;
for ( i = 0; i < stringlist_get_size( eclipseFiles ); i++ )
{
baseNameFiles->append( RiaStringEncodingTools::fromNativeEncoded( stringlist_safe_iget( eclipseFiles, i ) ) );
}
stringlist_free( eclipseFiles );
return baseNameFiles->count() > 0;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RifEclipseOutputFileTools::readGridDimensions( const QString& gridFileName, std::vector<std::vector<int>>& gridDimensions )
{
ecl_grid_type* grid = ecl_grid_alloc( RiaStringEncodingTools::toNativeEncoded( gridFileName ).data() ); // bootstrap
// ecl_grid
// instance
stringlist_type* lgr_names = ecl_grid_alloc_lgr_name_list( grid ); // get a list of all the lgr names.
// printf("grid:%s has %d a total of %d lgr's \n", grid_filename , stringlist_get_size( lgr_names ));
for ( int lgr_nr = 0; lgr_nr < stringlist_get_size( lgr_names ); lgr_nr++ )
{
ecl_grid_type* lgr_grid = ecl_grid_get_lgr( grid,
stringlist_iget( lgr_names,
lgr_nr ) ); // get the ecl_grid instance of the lgr
// - by name.
int nx, ny, nz, active_size;
ecl_grid_get_dims( lgr_grid, &nx, &ny, &nz, &active_size ); // get some size info from this lgr.
std::vector<int> values;
values.push_back( nx );
values.push_back( ny );
values.push_back( nz );
values.push_back( active_size );
gridDimensions.push_back( values );
}
ecl_grid_free( grid );
stringlist_free( lgr_names );
}
//--------------------------------------------------------------------------------------------------
/// Returns the following integer values from the first INTEHEAD keyword found
/// 1 : METRIC
/// 2 : FIELD
/// 3 : LAB
/// -1 : No INTEHEAD keyword found
//--------------------------------------------------------------------------------------------------
int RifEclipseOutputFileTools::readUnitsType( const ecl_file_type* ecl_file )
{
int unitsType = -1;
if ( ecl_file )
{
// ecl_file_iget_named_kw() does an unchecked vector access; guard against a missing keyword.
if ( ecl_file_get_num_named_kw( ecl_file, INTEHEAD_KW ) > 0 )
{
ecl_kw_type* kwINTEHEAD = ecl_file_iget_named_kw( ecl_file, INTEHEAD_KW, 0 );
if ( kwINTEHEAD )
{
unitsType = ecl_kw_iget_int( kwINTEHEAD, INTEHEAD_UNIT_INDEX );
}
}
}
return unitsType;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::ref<RifEclipseRestartDataAccess> RifEclipseOutputFileTools::createDynamicResultAccess( const QString& fileName )
{
QStringList filesWithSameBaseName;
RifEclipseOutputFileTools::findSiblingFilesWithSameBaseName( fileName, &filesWithSameBaseName );
cvf::ref<RifEclipseRestartDataAccess> resultsAccess;
// Look for unified restart file
auto unrstFileNames = RifEclipseOutputFileTools::filterFileNamesOfType( filesWithSameBaseName, ECL_UNIFIED_RESTART_FILE );
if ( !unrstFileNames.empty() )
{
QString unrstFileName = unrstFileNames.front();
if ( unrstFileNames.size() > 1 )
{
for ( const auto& candidate : unrstFileNames )
{
if ( candidate.endsWith( ".UNRST" ) ) unrstFileName = candidate;
}
}
resultsAccess = new RifEclipseUnifiedRestartFileAccess();
resultsAccess->setRestartFiles( QStringList( unrstFileName ) );
}
else
{
// Look for set of restart files (one file per time step)
QStringList restartFiles = RifEclipseOutputFileTools::filterFileNamesOfType( filesWithSameBaseName, ECL_RESTART_FILE );
if ( !restartFiles.empty() )
{
resultsAccess = new RifEclipseRestartFilesetAccess();
resultsAccess->setRestartFiles( restartFiles );
}
}
return resultsAccess;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RifEclipseOutputFileTools::createIndexFileName( const QString& resultFileName )
{
QFileInfo fi( resultFileName );
QString indexFileName = fi.path() + "/" + fi.completeBaseName() + ".RESINSIGHT_IDX";
return indexFileName;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<RiaDefines::PhaseType> RifEclipseOutputFileTools::findAvailablePhases( const ecl_file_type* ecl_file )
{
if ( ecl_file )
{
// ecl_file_iget_named_kw() does an unchecked vector access; guard against a missing keyword.
if ( ecl_file_get_num_named_kw( ecl_file, INTEHEAD_KW ) > 0 )
{
const ecl_kw_type* intehead = ecl_file_iget_named_kw( ecl_file, INTEHEAD_KW, 0 );
if ( intehead )
{
int phases = ecl_kw_iget_int( intehead, INTEHEAD_PHASE_INDEX );
return RiaOpmParserTools::phasesFromInteheadValue( phases );
}
}
}
return std::set<RiaDefines::PhaseType>();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RifEclipseOutputFileTools::transferNncFluxData( const ecl_grid_type* grid,
const ecl_file_view_type* summaryView,
std::vector<double>* waterFlux,
std::vector<double>* oilFlux,
std::vector<double>* gasFlux )
{
if ( !grid || !summaryView ) return;
ecl_nnc_geometry_type* nnc_geo = ecl_nnc_geometry_alloc( grid );
if ( nnc_geo )
{
ecl_nnc_data_type* waterFluxData = ecl_nnc_data_alloc_wat_flux( grid, nnc_geo, summaryView );
if ( waterFluxData )
{
const double* waterFluxValues = ecl_nnc_data_get_values( waterFluxData );
waterFlux->insert( waterFlux->end(), &waterFluxValues[0], &waterFluxValues[ecl_nnc_data_get_size( waterFluxData )] );
ecl_nnc_data_free( waterFluxData );
}
ecl_nnc_data_type* oilFluxData = ecl_nnc_data_alloc_oil_flux( grid, nnc_geo, summaryView );
if ( oilFluxData )
{
const double* oilFluxValues = ecl_nnc_data_get_values( oilFluxData );
oilFlux->insert( oilFlux->end(), &oilFluxValues[0], &oilFluxValues[ecl_nnc_data_get_size( oilFluxData )] );
ecl_nnc_data_free( oilFluxData );
}
ecl_nnc_data_type* gasFluxData = ecl_nnc_data_alloc_gas_flux( grid, nnc_geo, summaryView );
if ( gasFluxData )
{
const double* gasFluxValues = ecl_nnc_data_get_values( gasFluxData );
gasFlux->insert( gasFlux->end(), &gasFluxValues[0], &gasFluxValues[ecl_nnc_data_get_size( gasFluxData )] );
ecl_nnc_data_free( gasFluxData );
}
ecl_nnc_geometry_free( nnc_geo );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RifEclipseOutputFileTools::isExportedFromIntersect( const ecl_file_type* ecl_file )
{
// This code is taken from ecl_file_get_ecl_version() in ecl_file.cpp
// ecl_file_iget_named_kw() does an unchecked vector access; guard against a missing keyword.
if ( ecl_file_get_num_named_kw( ecl_file, INTEHEAD_KW ) == 0 ) return false;
ecl_kw_type* intehead_kw = ecl_file_iget_named_kw( ecl_file, INTEHEAD_KW, 0 );
if ( !intehead_kw ) return false;
int int_value = ecl_kw_iget_int( intehead_kw, INTEHEAD_IPROG_INDEX );
return int_value == INTEHEAD_INTERSECT_VALUE;
}
//--------------------------------------------------------------------------------------------------
/// Convenience method to hide C fopen calls in #pragma declarations to avoid warnings on Windows
//--------------------------------------------------------------------------------------------------
FILE* RifEclipseOutputFileTools::fopen( const QString& filePath, const QString& mode )
{
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 4996 )
#endif
FILE* filePtr =
std::fopen( RiaStringEncodingTools::toNativeEncoded( filePath ).data(), RiaStringEncodingTools::toNativeEncoded( mode ).data() );
#ifdef _MSC_VER
#pragma warning( pop )
#endif
return filePtr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RifEclipseOutputFileTools::assignActiveCellData( std::vector<std::vector<int>>& actnumValuesPerGrid, RigEclipseCaseData* eclipseCaseData )
{
size_t reservoirCellCount = 0;
for ( const auto& actnumValues : actnumValuesPerGrid )
{
reservoirCellCount += actnumValues.size();
}
// Check if number of cells is matching
if ( eclipseCaseData->mainGrid()->totalCellCount() != reservoirCellCount )
{
return false;
}
RigActiveCellInfo* activeCellInfo = eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
RigActiveCellInfo* fractureActiveCellInfo = eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL );
activeCellInfo->setReservoirCellCount( reservoirCellCount );
fractureActiveCellInfo->setReservoirCellCount( reservoirCellCount );
activeCellInfo->setGridCount( actnumValuesPerGrid.size() );
fractureActiveCellInfo->setGridCount( actnumValuesPerGrid.size() );
size_t cellIdx = 0;
size_t globalActiveMatrixIndex = 0;
size_t globalActiveFractureIndex = 0;
const int matrixActive = 1;
const int fractureActive = 2;
const int matrixAndFractureActive = 3;
for ( size_t gridIndex = 0; gridIndex < actnumValuesPerGrid.size(); gridIndex++ )
{
size_t activeMatrixIndex = 0;
size_t activeFractureIndex = 0;
std::vector<int>& actnumValues = actnumValuesPerGrid[gridIndex];
for ( int actnumValue : actnumValues )
{
if ( actnumValue == matrixActive || actnumValue == matrixAndFractureActive )
{
activeCellInfo->setCellResultIndex( ReservoirCellIndex( cellIdx ), ActiveCellIndex( globalActiveMatrixIndex++ ) );
activeMatrixIndex++;
}
if ( actnumValue == fractureActive || actnumValue == matrixAndFractureActive )
{
fractureActiveCellInfo->setCellResultIndex( ReservoirCellIndex( cellIdx ), ActiveCellIndex( globalActiveFractureIndex++ ) );
activeFractureIndex++;
}
cellIdx++;
}
activeCellInfo->setGridActiveCellCounts( gridIndex, activeMatrixIndex );
fractureActiveCellInfo->setGridActiveCellCounts( gridIndex, activeFractureIndex );
}
activeCellInfo->computeDerivedData();
fractureActiveCellInfo->computeDerivedData();
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RifEclipseReportKeywords RifEclipseOutputFileTools::createReportStepsMetaData( const std::vector<ecl_file_type*>& ecl_files )
{
RifEclipseReportKeywords reportSteps;
for ( auto ecl_file : ecl_files )
{
if ( !ecl_file ) continue;
int reportStepCount = ecl_file_get_num_named_kw( ecl_file, INTEHEAD_KW );
for ( int reportStepIndex = 0; reportStepIndex < reportStepCount; reportStepIndex++ )
{
ecl_file_view_type* rst_view = ecl_file_get_global_view( ecl_file );
if ( !rst_view ) continue;
ecl_rsthead_type* restart_header = ecl_rsthead_alloc( rst_view, reportStepIndex );
if ( restart_header )
{
ecl_file_push_block( ecl_file );
{
ecl_file_select_block( ecl_file, INTEHEAD_KW, reportStepIndex );
// Find number of keywords within this report step
int numKeywords = ecl_file_get_num_distinct_kw( ecl_file );
for ( int iKey = 0; iKey < numKeywords; iKey++ )
{
const char* kw = ecl_file_iget_distinct_kw( ecl_file, iKey );
int namedKeywordCount = ecl_file_get_num_named_kw( ecl_file, kw );
for ( int iOcc = 0; iOcc < namedKeywordCount; iOcc++ )
{
ecl_data_type dataTypeOnFile = ecl_file_iget_named_data_type( ecl_file, kw, iOcc );
ecl_type_enum dataTypeEnumOnFile = ecl_type_get_type( dataTypeOnFile );
if ( dataTypeEnumOnFile != ECL_DOUBLE_TYPE && dataTypeEnumOnFile != ECL_FLOAT_TYPE &&
dataTypeEnumOnFile != ECL_INT_TYPE )
{
continue;
}
auto dataType = RifEclipseKeywordValueCount::KeywordDataType::UNKNOWN;
if ( dataTypeEnumOnFile == ECL_DOUBLE_TYPE )
{
dataType = RifEclipseKeywordValueCount::KeywordDataType::DOUBLE;
}
else if ( dataTypeEnumOnFile == ECL_FLOAT_TYPE )
{
dataType = RifEclipseKeywordValueCount::KeywordDataType::FLOAT;
}
else if ( dataTypeEnumOnFile == ECL_INT_TYPE )
{
dataType = RifEclipseKeywordValueCount::KeywordDataType::INTEGER;
}
int itemCount = ecl_file_iget_named_size( ecl_file, kw, iOcc );
reportSteps.appendKeywordCount( kw, itemCount, dataType );
}
}
}
ecl_file_pop_block( ecl_file );
ecl_rsthead_free( restart_header );
}
}
}
return reportSteps;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RifEclipseKeywordValueCount>
RifEclipseOutputFileTools::validKeywordsForPorosityModel( const std::vector<RifEclipseKeywordValueCount>& keywordItemCounts,
const RigActiveCellInfo* matrixActiveCellInfo,
const RigActiveCellInfo* fractureActiveCellInfo,
RiaDefines::PorosityModelType porosityModel,
size_t timeStepCount )
{
if ( !matrixActiveCellInfo ) return {};
if ( porosityModel == RiaDefines::PorosityModelType::FRACTURE_MODEL && ( fractureActiveCellInfo->reservoirActiveCellCount() == 0 ) )
{
return {};
}
if ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL && ( matrixActiveCellInfo->reservoirActiveCellCount() == 0 ) )
{
return {};
}
std::vector<RifEclipseKeywordValueCount> keywordsWithCorrectNumberOfDataItems;
for ( const auto& keywordValueCount : keywordItemCounts )
{
QString keyword = QString::fromStdString( keywordValueCount.keyword() );
size_t valueCount = keywordValueCount.valueCount();
bool validKeyword = false;
auto matrixActiveCellCount = matrixActiveCellInfo->reservoirActiveCellCount();
auto fractureActiveCellCount = fractureActiveCellInfo->reservoirActiveCellCount();
if ( matrixActiveCellCount > 0 && ( valueCount % matrixActiveCellCount ) == 0 && valueCount <= timeStepCount * matrixActiveCellCount )
{
// Found result for all cells for N time steps, usually a static dataset for one time step
validKeyword = true;
}
else
{
size_t timeStepsMatrixRest = 0;
if ( matrixActiveCellCount > 0 )
{
timeStepsMatrixRest = valueCount % matrixActiveCellCount;
}
size_t timeStepsFractureRest = 0;
if ( fractureActiveCellCount > 0 )
{
timeStepsFractureRest = valueCount % fractureActiveCellCount;
}
size_t sumFractureMatrixActiveCellCount = matrixActiveCellCount + fractureActiveCellCount;
size_t timeStepsMatrixAndFractureRest = valueCount % sumFractureMatrixActiveCellCount;
if ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL && timeStepsMatrixRest == 0 )
{
if ( valueCount <= timeStepCount * std::max( matrixActiveCellCount, sumFractureMatrixActiveCellCount ) )
{
validKeyword = true;
}
}
else if ( porosityModel == RiaDefines::PorosityModelType::FRACTURE_MODEL && fractureActiveCellCount > 0 &&
timeStepsFractureRest == 0 )
{
if ( valueCount <= timeStepCount * std::max( fractureActiveCellCount, sumFractureMatrixActiveCellCount ) )
{
validKeyword = true;
}
}
else if ( timeStepsMatrixAndFractureRest == 0 )
{
if ( valueCount <= timeStepCount * sumFractureMatrixActiveCellCount )
{
validKeyword = true;
}
}
}
// is this a result with a value for all cells?
if ( !validKeyword )
{
if ( valueCount > 0 && ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL ) &&
matrixActiveCellInfo->reservoirCellCount() > 0 && ( valueCount % matrixActiveCellInfo->reservoirCellCount() == 0 ) )
{
validKeyword = true;
}
}
// Check for INIT values that has only values for main grid active cells
if ( !validKeyword )
{
if ( timeStepCount == 1 )
{
size_t mainGridMatrixActiveCellCount = matrixActiveCellInfo->reservoirActiveCellCount();
size_t mainGridFractureActiveCellCount = fractureActiveCellInfo->reservoirActiveCellCount();
if ( valueCount == mainGridMatrixActiveCellCount || valueCount == mainGridFractureActiveCellCount ||
valueCount == mainGridMatrixActiveCellCount + mainGridFractureActiveCellCount )
{
validKeyword = true;
}
}
}
if ( validKeyword )
{
keywordsWithCorrectNumberOfDataItems.push_back( keywordValueCount );
}
}
return keywordsWithCorrectNumberOfDataItems;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RifEclipseOutputFileTools::extractResultValuesBasedOnPorosityModel( RigEclipseCaseData* eclipseCaseData,
RiaDefines::PorosityModelType matrixOrFracture,
std::vector<double>* destinationResultValues,
const std::vector<double>& sourceResultValues )
{
if ( sourceResultValues.empty() ) return;
RigActiveCellInfo* fracActCellInfo = eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL );
if ( matrixOrFracture == RiaDefines::PorosityModelType::MATRIX_MODEL && fracActCellInfo->reservoirActiveCellCount() == 0 )
{
destinationResultValues->insert( destinationResultValues->end(), sourceResultValues.begin(), sourceResultValues.end() );
}
else
{
RigActiveCellInfo* actCellInfo = eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
size_t sourceStartPosition = 0;
for ( size_t i = 0; i < eclipseCaseData->mainGrid()->gridCount(); i++ )
{
if ( eclipseCaseData->mainGrid()->gridByIndex( i )->isTempGrid() ) continue;
size_t matrixActiveCellCount = actCellInfo->gridActiveCellCounts( i );
size_t fractureActiveCellCount = fracActCellInfo->gridActiveCellCounts( i );
if ( matrixOrFracture == RiaDefines::PorosityModelType::MATRIX_MODEL )
{
destinationResultValues->insert( destinationResultValues->end(),
sourceResultValues.begin() + sourceStartPosition,
sourceResultValues.begin() + sourceStartPosition + matrixActiveCellCount );
}
else
{
if ( ( matrixActiveCellCount + fractureActiveCellCount ) > sourceResultValues.size() )
{
// Special handling of the situation where we only have data for one fracture mode
matrixActiveCellCount = 0;
}
destinationResultValues->insert( destinationResultValues->end(),
sourceResultValues.begin() + sourceStartPosition + matrixActiveCellCount,
sourceResultValues.begin() + sourceStartPosition + matrixActiveCellCount +
fractureActiveCellCount );
}
sourceStartPosition += ( matrixActiveCellCount + fractureActiveCellCount );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::optional<RiaDefines::EclipseUnitSystem> RifEclipseOutputFileTools::unitValueToEnum( int unitValue )
{
if ( unitValue == 1 )
return RiaDefines::EclipseUnitSystem::UNITS_METRIC;
else if ( unitValue == 2 )
return RiaDefines::EclipseUnitSystem::UNITS_FIELD;
else if ( unitValue == 3 )
return RiaDefines::EclipseUnitSystem::UNITS_LAB;
return std::nullopt;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaDefines::EclipseUnitSystem RifEclipseOutputFileTools::determineUnitSystem( const std::optional<RiaDefines::EclipseUnitSystem>& egridUnit,
const std::optional<RiaDefines::EclipseUnitSystem>& initUnit,
const std::optional<RiaDefines::EclipseUnitSystem>& unrstUnit )
{
// Helper lambda to get unit name string
auto getUnitName = []( RiaDefines::EclipseUnitSystem unit ) -> QString
{
if ( unit == RiaDefines::EclipseUnitSystem::UNITS_METRIC ) return "METRIC";
if ( unit == RiaDefines::EclipseUnitSystem::UNITS_FIELD ) return "FIELD";
if ( unit == RiaDefines::EclipseUnitSystem::UNITS_LAB ) return "LAB";
return "UNKNOWN";
};
// Determine which unit to use based on priority: egrid > init > unrst
std::optional<RiaDefines::EclipseUnitSystem> selectedUnit;
QString selectedSource;
if ( egridUnit.has_value() )
{
selectedUnit = egridUnit;
selectedSource = "EGRID";
}
else if ( initUnit.has_value() )
{
selectedUnit = initUnit;
selectedSource = "INIT";
}
else if ( unrstUnit.has_value() )
{
selectedUnit = unrstUnit;
selectedSource = "UNRST";
}
// Check for mismatches and warn
if ( selectedUnit.has_value() )
{
// Build list of sources that differ from selected
QStringList conflicts;
if ( egridUnit.has_value() && initUnit.has_value() && egridUnit.value() != initUnit.value() )
{
conflicts.append( QString( "INIT file has %1" ).arg( getUnitName( initUnit.value() ) ) );
}
if ( egridUnit.has_value() && unrstUnit.has_value() && egridUnit.value() != unrstUnit.value() )
{
conflicts.append( QString( "UNRST file has %1" ).arg( getUnitName( unrstUnit.value() ) ) );
}
if ( !egridUnit.has_value() && initUnit.has_value() && unrstUnit.has_value() && initUnit.value() != unrstUnit.value() )
{
conflicts.append( QString( "UNRST file has %1" ).arg( getUnitName( unrstUnit.value() ) ) );
}
if ( !conflicts.isEmpty() )
{
QString warning = QString( "Unit system mismatch detected: Using %1 from %2 file, but %3. "
"Please verify your simulation files have consistent unit systems." )
.arg( getUnitName( selectedUnit.value() ) )
.arg( selectedSource )
.arg( conflicts.join( " and " ) );
RiaLogging::warning( warning.toStdString() );
}
return selectedUnit.value();
}
// Default to METRIC if no unit found
return RiaDefines::EclipseUnitSystem::UNITS_METRIC;
}