#14964: Use correct depth unit when creating RFT plots

Read the summary unit system from SMSPEC INTEHEAD, and fall back to vector
units for ESMRY files without SMSPEC. The unit system is read on first
request to keep summary import fast. Ensembles use the same realization as
for addresses. New RFT plots get the depth unit from their data sources.
This commit is contained in:
Magne Sjaastad authored and GitHub committed 2026-10-19 14:15:55 +02:00
1 parent eb66255a70
commit 9229e48191
12 files changed
+230 -8

No files matched your search

@@ -146,6 +146,13 @@ std::vector<RiaDefines::EclipseUnitSystem> RifDataSourceForRftPlt::availableUnit
systems.push_back( m_eclCase->eclipseCaseData()->unitsType() );
}
auto addIfKnown = [&systems]( RiaDefines::EclipseUnitSystem system )
{
if ( system != RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN ) systems.push_back( system );
};
if ( m_summaryCase ) addIfKnown( m_summaryCase->unitsSystem() );
if ( m_ensemble ) addIfKnown( m_ensemble->unitSystem() );
if ( m_wellLogFile && m_wellLogFile->wellLogData() )
{
auto eclipseUnit = RiaDefines::fromDepthUnit( m_wellLogFile->wellLogData()->depthUnit() );
@@ -132,7 +132,20 @@ RiaDefines::EclipseUnitSystem RifMultipleSummaryReaders::unitSystem() const
{
CAF_ASSERT( !m_readers.empty() );
return m_readers.at( 0 )->unitSystem();
// Readers without unit information are ignored, conflicting readers give an unknown unit system
auto unitSystem = RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
for ( const auto& reader : m_readers )
{
auto readerUnitSystem = reader->unitSystem();
if ( readerUnitSystem == RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN ) continue;
if ( unitSystem == RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN )
unitSystem = readerUnitSystem;
else if ( unitSystem != readerUnitSystem )
return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
}
return unitSystem;
}
//--------------------------------------------------------------------------------------------------
@@ -179,6 +179,9 @@ bool RifOpmCommonEclipseSummary::open( const QString& fileName, bool includeRest
populateTimeSteps();
if ( isLoggingEnabled ) RiaLogging::logElapsedTime( "Completed populateTimeSteps", timeBeforeTimeSteps );
m_fileName = fileName;
m_unitSystem = std::nullopt;
return true;
}
@@ -251,8 +254,55 @@ std::string RifOpmCommonEclipseSummary::unitName( const RifEclipseSummaryAddress
//--------------------------------------------------------------------------------------------------
RiaDefines::EclipseUnitSystem RifOpmCommonEclipseSummary::unitSystem() const
{
// TODO: Not implemented
return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
if ( !m_unitSystem ) m_unitSystem = detectUnitSystem();
return *m_unitSystem;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaDefines::EclipseUnitSystem RifOpmCommonEclipseSummary::detectUnitSystem() const
{
auto smspecFileName = RifOpmSummaryTools::smspecSummaryFilename( m_fileName );
auto unitSystem = RifOpmSummaryTools::readUnitSystem( smspecFileName );
// ESMRY files can be imported without SMSPEC, and ESMRY has no unit system flag
if ( unitSystem == RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN && m_enhancedReader ) return unitSystemFromVectorUnits();
return unitSystem;
}
//--------------------------------------------------------------------------------------------------
/// Units from different systems can be mixed (PVT-M, user defined vectors), so only accept a single match
//--------------------------------------------------------------------------------------------------
RiaDefines::EclipseUnitSystem RifOpmCommonEclipseSummary::unitSystemFromVectorUnits() const
{
static const std::map<std::string, RiaDefines::EclipseUnitSystem> unitToSystem = {
{ "PSIA", RiaDefines::EclipseUnitSystem::UNITS_FIELD },
{ "STB", RiaDefines::EclipseUnitSystem::UNITS_FIELD },
{ "STB/DAY", RiaDefines::EclipseUnitSystem::UNITS_FIELD },
{ "MSCF/DAY", RiaDefines::EclipseUnitSystem::UNITS_FIELD },
{ "BARSA", RiaDefines::EclipseUnitSystem::UNITS_METRIC },
{ "SM3", RiaDefines::EclipseUnitSystem::UNITS_METRIC },
{ "SM3/DAY", RiaDefines::EclipseUnitSystem::UNITS_METRIC },
{ "ATMA", RiaDefines::EclipseUnitSystem::UNITS_LAB },
{ "SCC", RiaDefines::EclipseUnitSystem::UNITS_LAB },
};
auto detected = RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
for ( const auto& keyword : m_enhancedReader->keywordList() )
{
const auto unit = std::string( RiaStdStringTools::trimString( m_enhancedReader->get_unit( keyword ) ) );
auto it = unitToSystem.find( unit );
if ( it == unitToSystem.end() ) continue;
if ( detected == RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN )
detected = it->second;
else if ( detected != it->second )
return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
}
return detected;
}
//--------------------------------------------------------------------------------------------------
@@ -26,6 +26,7 @@
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <tuple>
@@ -75,6 +76,9 @@ private:
void populateTimeSteps();
std::string keywordForAddress( const RifEclipseSummaryAddress& address ) const;
RiaDefines::EclipseUnitSystem detectUnitSystem() const;
RiaDefines::EclipseUnitSystem unitSystemFromVectorUnits() const;
static bool writeEsmryFile( QString& smspecFileName, bool includeRestartFiles, RiaThreadSafeLogger* threadSafeLogger );
static void increaseEsmryFileCount();
@@ -90,5 +94,9 @@ private:
bool m_useEsmryFiles;
bool m_createEsmryFiles;
// Detected on first request, as reading the SMSPEC file is not needed for summary import
QString m_fileName;
mutable std::optional<RiaDefines::EclipseUnitSystem> m_unitSystem;
RifEnsembleImportConfig m_ensembleImportState;
};
@@ -98,6 +98,9 @@ bool RifOpmHdf5Summary::open( const QString& headerFileName, bool includeRestart
createAndSetAddresses();
m_headerFileName = headerFileName;
m_unitSystem = std::nullopt;
return true;
}
@@ -158,8 +161,11 @@ std::string RifOpmHdf5Summary::unitName( const RifEclipseSummaryAddress& resultA
//--------------------------------------------------------------------------------------------------
RiaDefines::EclipseUnitSystem RifOpmHdf5Summary::unitSystem() const
{
// TODO: Not implemented
return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
if ( !m_unitSystem )
{
m_unitSystem = RifOpmSummaryTools::readUnitSystem( RifOpmSummaryTools::smspecSummaryFilename( m_headerFileName ) );
}
return *m_unitSystem;
}
//--------------------------------------------------------------------------------------------------
@@ -28,6 +28,7 @@
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>
@@ -73,4 +74,8 @@ private:
std::vector<time_t> m_timeSteps;
std::unique_ptr<RifHdf5SummaryReader> m_hdf5Reader;
// Detected on first request, as reading the SMSPEC file is not needed for summary import
QString m_headerFileName;
mutable std::optional<RiaDefines::EclipseUnitSystem> m_unitSystem;
};
@@ -23,11 +23,14 @@
#include "RiaPreferencesSummary.h"
#include "RiaQStringFormatter.h"
#include "RifEclipseOutputFileTools.h"
#ifdef _MSC_VER
// Disable warning from external library to make sure treat warnings as error works
#pragma warning( disable : 4267 )
#endif
#include "opm/io/eclipse/ESmry.hpp"
#include "opm/io/eclipse/EclFile.hpp"
#include "opm/io/eclipse/ExtESmry.hpp"
#include <QFile>
@@ -222,6 +225,33 @@ bool RifOpmSummaryTools::isEsmryConversionRequired( const QString& fileName )
return false;
}
//--------------------------------------------------------------------------------------------------
/// The unit system is stored in INTEHEAD[0] of the SMSPEC file
//--------------------------------------------------------------------------------------------------
RiaDefines::EclipseUnitSystem RifOpmSummaryTools::readUnitSystem( const QString& smspecFileName )
{
if ( !QFile::exists( smspecFileName ) ) return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
try
{
Opm::EclIO::EclFile smspecFile( smspecFileName.toStdString() );
if ( smspecFile.hasKey( "INTEHEAD" ) )
{
const auto& intehead = smspecFile.get<int>( "INTEHEAD" );
if ( !intehead.empty() )
{
return RifEclipseOutputFileTools::unitValueToEnum( intehead[0] ).value_or( RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN );
}
}
}
catch ( const std::exception& e )
{
RiaLogging::debug( QString( "Failed to read unit system from %1: %2" ).arg( smspecFileName, e.what() ).toStdString() );
}
return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -18,6 +18,8 @@
#pragma once
#include "RiaDefines.h"
#include "RifEclipseSummaryAddress.h"
#include <QString>
@@ -51,6 +53,8 @@ QString enhancedSummaryFilename( const QString& fileName );
QString smspecSummaryFilename( const QString& fileName );
bool isEsmryConversionRequired( const QString& fileName );
RiaDefines::EclipseUnitSystem readUnitSystem( const QString& smspecFileName );
std::expected<int, QString> extractRealizationNumber( const QString& path );
}; // namespace RifOpmSummaryTools
@@ -1917,6 +1917,21 @@ void RimWellRftPlot::initializeDataSources( RimWellRftPlot* source )
{
// If no source is provided, initialize with empty sources
setOrInitializeDataSources( {} );
// Use the depth unit of the data in a new plot, with the unit of the grid model in the project as fallback
auto depthUnit = RiaDefines::DepthUnitType::UNIT_NONE;
for ( const auto& dataSource : m_selectedSources() )
{
for ( auto unitSystem : dataSource.availableUnitSystems() )
{
depthUnit = RiaDefines::fromEclipseUnit( unitSystem );
if ( depthUnit != RiaDefines::DepthUnitType::UNIT_NONE ) break;
}
if ( depthUnit != RiaDefines::DepthUnitType::UNIT_NONE ) break;
}
if ( depthUnit == RiaDefines::DepthUnitType::UNIT_NONE ) depthUnit = caseDepthUnit();
if ( depthUnit != RiaDefines::DepthUnitType::UNIT_NONE ) setDepthUnit( depthUnit );
}
}
@@ -750,11 +750,14 @@ bool RimSummaryEnsemble::operator<( const RimSummaryEnsemble& rhs ) const
//--------------------------------------------------------------------------------------------------
RiaDefines::EclipseUnitSystem RimSummaryEnsemble::unitSystem() const
{
if ( m_cases.empty() )
// The first realization can be missing data, use the first realization with a known unit system
for ( auto* summaryCase : m_cases.childrenByType() )
{
return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
auto unitSystem = summaryCase->unitsSystem();
if ( unitSystem != RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN ) return unitSystem;
}
return m_cases[0]->unitsSystem();
return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
}
//--------------------------------------------------------------------------------------------------
@@ -31,6 +31,27 @@ public:
size_t keywordCount() const override { return m_allResultAddresses.size(); }
};
//--------------------------------------------------------------------------------------------------
/// Stand-in for a reader reporting a given unit system
//--------------------------------------------------------------------------------------------------
class StubUnitSystemReader : public RifSummaryReaderInterface
{
public:
explicit StubUnitSystemReader( RiaDefines::EclipseUnitSystem unitSystem )
: m_unitSystem( unitSystem )
{
}
std::vector<time_t> timeSteps( const RifEclipseSummaryAddress& resultAddress ) const override { return {}; }
std::pair<bool, std::vector<double>> values( const RifEclipseSummaryAddress& resultAddress ) const override { return { false, {} }; }
std::string unitName( const RifEclipseSummaryAddress& resultAddress ) const override { return {}; }
RiaDefines::EclipseUnitSystem unitSystem() const override { return m_unitSystem; }
size_t keywordCount() const override { return 0; }
private:
RiaDefines::EclipseUnitSystem m_unitSystem;
};
//--------------------------------------------------------------------------------------------------
/// Create a calculation producing a single field address
//--------------------------------------------------------------------------------------------------
@@ -122,3 +143,25 @@ TEST( RifMultipleSummaryReaders, RefreshCalculatedAddressesForReaderWithNoAddres
RimProject::current()->calculationCollection()->deleteCalculation( calculation );
}
//--------------------------------------------------------------------------------------------------
/// Readers without unit information are ignored, and readers with conflicting units give an unknown unit system
//--------------------------------------------------------------------------------------------------
TEST( RifMultipleSummaryReaders, UnitSystemFromMultipleReaders )
{
using Units = RiaDefines::EclipseUnitSystem;
{
RifMultipleSummaryReaders readers;
readers.addReader( std::make_unique<StubUnitSystemReader>( Units::UNITS_UNKNOWN ) );
readers.addReader( std::make_unique<StubUnitSystemReader>( Units::UNITS_FIELD ) );
EXPECT_EQ( Units::UNITS_FIELD, readers.unitSystem() );
}
{
RifMultipleSummaryReaders readers;
readers.addReader( std::make_unique<StubUnitSystemReader>( Units::UNITS_FIELD ) );
readers.addReader( std::make_unique<StubUnitSystemReader>( Units::UNITS_METRIC ) );
EXPECT_EQ( Units::UNITS_UNKNOWN, readers.unitSystem() );
}
}
@@ -160,6 +160,44 @@ TEST( OpmSummaryTests, ReadRegionToRegionValuesWithoutPrebuiltAddressMap )
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( OpmSummaryTests, ReadUnitSystem )
{
{
RifOpmCommonEclipseSummary reader;
reader.useEnhancedSummaryFiles( false );
reader.createEnhancedSummaryFiles( false );
ASSERT_TRUE( reader.open( H5_TEST_DATA_DIRECTORY + "NORNE_ATW2013_RFTPLT_V2.SMSPEC", false, nullptr ) );
EXPECT_EQ( RiaDefines::EclipseUnitSystem::UNITS_METRIC, reader.unitSystem() );
}
{
// ESMRY without SMSPEC, the unit system is derived from the vector units. Create the ESMRY file in a temporary folder, as the
// ESMRY file in the test data folder is rewritten by other tests.
QTemporaryDir tempDir;
ASSERT_TRUE( tempDir.isValid() );
const QString smspecFilePath = tempDir.filePath( "NORNE_ATW2013_RFTPLT_V2.SMSPEC" );
const QString unsmryFilePath = tempDir.filePath( "NORNE_ATW2013_RFTPLT_V2.UNSMRY" );
const QString esmryFilePath = tempDir.filePath( "NORNE_ATW2013_RFTPLT_V2.ESMRY" );
ASSERT_TRUE( QFile::copy( H5_TEST_DATA_DIRECTORY + "NORNE_ATW2013_RFTPLT_V2.SMSPEC", smspecFilePath ) );
ASSERT_TRUE( QFile::copy( H5_TEST_DATA_DIRECTORY + "NORNE_ATW2013_RFTPLT_V2.UNSMRY", unsmryFilePath ) );
{
Opm::EclIO::ESmry eSmry( smspecFilePath.toStdString() );
ASSERT_TRUE( eSmry.make_esmry_file() );
}
ASSERT_TRUE( QFile::remove( smspecFilePath ) );
ASSERT_TRUE( QFile::remove( unsmryFilePath ) );
RifOpmCommonEclipseSummary reader;
reader.useEnhancedSummaryFiles( true );
reader.createEnhancedSummaryFiles( false );
ASSERT_TRUE( reader.open( esmryFilePath, false, nullptr ) );
EXPECT_EQ( RiaDefines::EclipseUnitSystem::UNITS_METRIC, reader.unitSystem() );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------