#14391 Expose RMS seed value from RMS_SEED_USED file as a realization parameter

This commit is contained in:
Magne Sjaastad authored and GitHub committed 2026-08-03 10:55:07 +02:00
1 parent 69faf4ac13
commit 32cfac7722
13 files changed
+218 -6

No files matched your search

@@ -194,6 +194,14 @@ QString RiaDefines::summaryRealizationNumber()
return "RI:REALIZATION_NUM";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiaDefines::summaryRmsSeed()
{
return "RMS_SEED_USED";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -68,6 +68,7 @@ QString summaryLgrBlock();
QString summaryCalculated();
QString summaryRealizationNumber();
QString summaryRmsSeed();
QString key1VariableName();
QString key2VariableName();
@@ -26,6 +26,8 @@
#include "RifOpmSummaryTools.h"
#include <QDir>
#include <QFileInfo>
#include <QRegularExpression>
#include <QString>
#include <QStringList>
@@ -281,6 +283,75 @@ QString RifCaseRealizationParametersFileLocator::locate( const QString& modelPat
return "";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RifRmsSeedFileReader::fileName()
{
return "RMS_SEED_USED";
}
//--------------------------------------------------------------------------------------------------
/// Locate the RMS seed file "rms/model/RMS_SEED_USED" relative to one of the parent directories of
/// the given model file or folder, i.e. "<ensemble>/realization-N/iter-M/rms/model/RMS_SEED_USED"
//--------------------------------------------------------------------------------------------------
QString RifRmsSeedFileReader::locate( const QString& modelPath )
{
int MAX_LEVELS_UP = 5;
int dirLevel = 0;
QDir qdir( modelPath );
const QFileInfo dir( modelPath );
if ( dir.isFile() )
qdir.cdUp();
else if ( !dir.isDir() )
return "";
do
{
QString candidate = qdir.absoluteFilePath( "rms/model/" + fileName() );
if ( QFileInfo::exists( candidate ) ) return candidate;
qdir.cdUp();
} while ( dirLevel++ < MAX_LEVELS_UP );
return "";
}
//--------------------------------------------------------------------------------------------------
/// Read the RMS seed value from the last token of the last non-empty line, allowing the value to be
/// preceded by a timestamp or other text: "25-07-2025 10:33:06 ... 81472369"
//--------------------------------------------------------------------------------------------------
std::optional<double> RifRmsSeedFileReader::readSeedValue( const QString& filePath )
{
QFile file( filePath );
if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) ) return std::nullopt;
QTextStream dataStream( &file );
QString lastNonEmptyLine;
while ( !dataStream.atEnd() )
{
QString line = dataStream.readLine().trimmed();
if ( !line.isEmpty() ) lastNonEmptyLine = line;
}
if ( lastNonEmptyLine.isEmpty() ) return std::nullopt;
const QStringList tokens = RiaTextStringTools::splitSkipEmptyParts( lastNonEmptyLine, QRegularExpression( "\\s+" ) );
if ( tokens.isEmpty() ) return std::nullopt;
const auto valueText = tokens.back().toStdString();
if ( !RiaStdStringTools::isNumber( valueText, RiaStdStringTools::decimalPoint() ) ) return std::nullopt;
double value = 0.0;
if ( !RiaStdStringTools::toDouble( valueText, value ) ) return std::nullopt;
return value;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -28,6 +28,7 @@
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>
@@ -95,3 +96,15 @@ public:
static QString locate( const QString& modelPath );
static int realizationNumber( const QString& modelPath );
};
//==================================================================================================
//
//
//==================================================================================================
class RifRmsSeedFileReader
{
public:
static QString fileName();
static QString locate( const QString& modelPath );
static std::optional<double> readSeedValue( const QString& filePath );
};
@@ -60,12 +60,15 @@ void RifEnsembleImportConfig::computePatternsFromSummaryFilePaths( const QString
auto restartFileNames1 = RifEclipseSummaryTools::getRestartFileNames( filePath1, warnings );
auto paramFilePath1 = RifCaseRealizationParametersFileLocator::locate( filePath1 );
auto rmsSeedFilePath1 = RifRmsSeedFileReader::locate( filePath1 );
auto restartFileNames2 = RifEclipseSummaryTools::getRestartFileNames( filePath2, warnings );
auto paramFilePath2 = RifCaseRealizationParametersFileLocator::locate( filePath2 );
auto rmsSeedFilePath2 = RifRmsSeedFileReader::locate( filePath2 );
computeRestartPatternsFromTwoRealizations( restartFileNames1, restartFileNames2 );
computeParameterFilePathPattern( { paramFilePath1, paramFilePath2 } );
computeRmsSeedFilePathPattern( { rmsSeedFilePath1, rmsSeedFilePath2 } );
m_useConfigValues = true;
}
@@ -138,6 +141,40 @@ void RifEnsembleImportConfig::computeParameterFilePathPattern( const std::vector
m_parameterFilePathPattern = pattern;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RifEnsembleImportConfig::pathToRmsSeedFile( int realizationNumber ) const
{
QString numberString = QString::number( realizationNumber );
auto rmsSeedFilePath = m_rmsSeedFilePathPattern;
rmsSeedFilePath.replace( placeholderText(), numberString );
return rmsSeedFilePath;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RifEnsembleImportConfig::computeRmsSeedFilePathPattern( const std::vector<QString>& filePaths )
{
m_rmsSeedFilePathPattern.clear();
// The RMS seed file is optional, keep the pattern empty without reporting errors if it is not found
QStringList filePathsList;
for ( const auto& filePath : filePaths )
{
if ( filePath.isEmpty() ) return;
filePathsList.push_back( filePath );
}
if ( filePathsList.size() < 2 ) return;
const auto [pattern, range] = RiaEnsembleImportTools::findPathPattern( filePathsList, RifEnsembleImportConfig::placeholderText() );
m_rmsSeedFilePathPattern = pattern;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -29,6 +29,7 @@ public:
std::vector<QString> restartFilesForRealization( int realizationNumber ) const;
QString pathToParameterFile( int realizationNumber ) const;
QString pathToRmsSeedFile( int realizationNumber ) const;
bool useConfigValues() const;
private:
@@ -36,6 +37,7 @@ private:
const std::vector<QString>& restartFilesSecondCase );
void computeParameterFilePathPattern( const std::vector<QString>& filePaths );
void computeRmsSeedFilePathPattern( const std::vector<QString>& filePaths );
static QString placeholderText();
@@ -43,4 +45,5 @@ private:
bool m_useConfigValues = false;
std::vector<QString> m_restartFileNamePatterns;
QString m_parameterFilePathPattern;
QString m_rmsSeedFilePathPattern;
};
@@ -62,13 +62,18 @@
#include <QDir>
#include <atomic>
#include <memory>
#include <optional>
CAF_PDM_SOURCE_INIT( RimSummaryCaseMainCollection, "SummaryCaseCollection" );
//--------------------------------------------------------------------------------------------------
/// Internal function
/// Internal function. An empty rmsSeedFileCandidate means the RMS seed file is known to be absent, while std::nullopt
/// means the file system must be searched for the file.
//--------------------------------------------------------------------------------------------------
void addCaseRealizationParametersIfFound( RimSummaryCase& sumCase, const QString modelFolderOrFile, const QString& filePathCandidate )
void addCaseRealizationParametersIfFound( RimSummaryCase& sumCase,
const QString modelFolderOrFile,
const QString& filePathCandidate,
const std::optional<QString>& rmsSeedFileCandidate )
{
std::shared_ptr<RigCaseRealizationParameters> parameters;
@@ -90,7 +95,8 @@ void addCaseRealizationParametersIfFound( RimSummaryCase& sumCase, const QString
}
}
}
else
if ( !parameters )
{
parameters = std::make_shared<RigCaseRealizationParameters>();
}
@@ -101,6 +107,18 @@ void addCaseRealizationParametersIfFound( RimSummaryCase& sumCase, const QString
parameters->setRealizationNumber( realizationNumber );
parameters->addParameter( RiaDefines::summaryRealizationNumber(), realizationNumber );
// If present, expose the RMS seed value from "rms/model/RMS_SEED_USED" as a realization parameter. Ignore missing
// or malformed files silently.
QString rmsSeedFile = rmsSeedFileCandidate.has_value() ? rmsSeedFileCandidate.value()
: RifRmsSeedFileReader::locate( modelFolderOrFile );
if ( !rmsSeedFile.isEmpty() )
{
if ( auto seedValue = RifRmsSeedFileReader::readSeedValue( rmsSeedFile ) )
{
parameters->addParameter( RiaDefines::summaryRmsSeed(), seedValue.value() );
}
}
sumCase.setCaseRealizationParameters( parameters );
}
}
@@ -495,7 +513,7 @@ void RimSummaryCaseMainCollection::loadSummaryCaseData( const std::vector<RimSum
{
sumCase->createSummaryReaderInterface();
sumCase->createRftReaderInterface();
addCaseRealizationParametersIfFound( *sumCase, sumCase->summaryHeaderFilename(), {} );
addCaseRealizationParametersIfFound( *sumCase, sumCase->summaryHeaderFilename(), {}, std::nullopt );
}
{
@@ -595,18 +613,20 @@ void RimSummaryCaseMainCollection::loadFileSummaryCaseData( const std::vector<Ri
{
fileSummaryCase->createSummaryReaderInterfaceThreadSafe( importState, &threadSafeLogger );
QString parameterFilePath;
QString parameterFilePath;
std::optional<QString> rmsSeedFilePath;
if ( importState.useConfigValues() )
{
auto realizationNumber = RifOpmSummaryTools::extractRealizationNumber( fileSummaryCase->summaryHeaderFilename() );
if ( realizationNumber.has_value() )
{
parameterFilePath = importState.pathToParameterFile( realizationNumber.value() );
rmsSeedFilePath = importState.pathToRmsSeedFile( realizationNumber.value() );
}
}
auto startTime = RiaLogging::currentTime();
addCaseRealizationParametersIfFound( *fileSummaryCase, fileSummaryCase->summaryHeaderFilename(), parameterFilePath );
addCaseRealizationParametersIfFound( *fileSummaryCase, fileSummaryCase->summaryHeaderFilename(), parameterFilePath, rmsSeedFilePath );
bool isLoggingEnabled = RiaPreferencesSystem::current()->isLoggingActivatedForKeyword( "OpmSummaryImport" );
if ( isLoggingEnabled )
{
@@ -95,6 +95,58 @@ TEST( RifCaseRealizationParametersReaderTest, SuccessfulParsing )
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RifRmsSeedFileReaderTest, LocatorTestSuccess )
{
QString modelDir = CASE_REAL_TEST_DATA_DIRECTORY_01 + "rms_seed/realization-1/iter-0/eclipse/model";
QString file = RifRmsSeedFileReader::locate( modelDir );
QString expected = CASE_REAL_TEST_DATA_DIRECTORY_01 + "rms_seed/realization-1/iter-0/rms/model/RMS_SEED_USED";
EXPECT_EQ( expected.toStdString(), file.toStdString() );
auto seedValue = RifRmsSeedFileReader::readSeedValue( file );
EXPECT_TRUE( seedValue.has_value() );
EXPECT_EQ( 81472369.0, seedValue.value() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RifRmsSeedFileReaderTest, LocatorTestFailure )
{
QString file = RifRmsSeedFileReader::locate( CASE_REAL_TEST_DATA_DIRECTORY_01 + "4/3/2" );
EXPECT_TRUE( file.isEmpty() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RifRmsSeedFileReaderTest, ReadSeedValue )
{
{
auto seedValue =
RifRmsSeedFileReader::readSeedValue( CASE_REAL_TEST_DATA_DIRECTORY_01 + "rms_seed_files/RMS_SEED_USED_single_value" );
EXPECT_TRUE( seedValue.has_value() );
EXPECT_EQ( 81472369.0, seedValue.value() );
}
{
// Use the seed value from the last non-empty line
auto seedValue = RifRmsSeedFileReader::readSeedValue( CASE_REAL_TEST_DATA_DIRECTORY_01 + "rms_seed_files/RMS_SEED_USED_multiline" );
EXPECT_TRUE( seedValue.has_value() );
EXPECT_EQ( 81472369.0, seedValue.value() );
}
{
auto seedValue = RifRmsSeedFileReader::readSeedValue( CASE_REAL_TEST_DATA_DIRECTORY_01 + "rms_seed_files/RMS_SEED_USED_invalid" );
EXPECT_FALSE( seedValue.has_value() );
}
{
auto seedValue = RifRmsSeedFileReader::readSeedValue( CASE_REAL_TEST_DATA_DIRECTORY_01 + "rms_seed_files/does_not_exist" );
EXPECT_FALSE( seedValue.has_value() );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -0,0 +1 @@
Placeholder file to make sure this directory is added to git
@@ -0,0 +1,3 @@
22-07-2025 08:00:00 ... 11111111
25-07-2025 10:33:06 ... 81472369