mirror of
https://github.com/OPM/ResInsight.git
synced 2026-08-27 05:37:21 -05:00
Fix monotonicity of depth values in extendDepthTable and add tests
Only prepend/append padding rows to RSVD/RVVD/RTEMPVD/PBVD/PDVD tables when the existing table does not already cover the padding depth range. This prevents non-monotonic depth sequences that caused issues in OPM. Also expose extendDepthTable as a public static method on RigPadModel and add unit tests for all extension scenarios. Co-authored-by: Vegard Kippe <vkip@equinor.com>
This commit is contained in:
co-authored by
Vegard Kippe
parent
52329d567d
commit
0aee9033ac
@@ -788,10 +788,12 @@ void extendEQUIL( Opm::DeckKeyword& equil )
|
||||
equil.addRecord( std::move( record ) );
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Extend RSVD/RVVD/RTEMPVD/PBVD/PDVD tables (padmodel lines 832-867)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void extendDepthTable( const RigModelPaddingSettings& settings, Opm::DeckKeyword& propertyVD )
|
||||
void RigPadModel::extendDepthTable( const RigModelPaddingSettings& settings, Opm::DeckKeyword& propertyVD )
|
||||
{
|
||||
assert( !propertyVD.empty() );
|
||||
|
||||
@@ -804,16 +806,25 @@ void extendDepthTable( const RigModelPaddingSettings& settings, Opm::DeckKeyword
|
||||
|
||||
const auto& values = table.getItem( 0 ).getData<double>();
|
||||
|
||||
i.front().push_back( settings.topUpper() );
|
||||
i.front().push_back( values[0 + 1] );
|
||||
// Need at least one depth-property pair for safe indexing
|
||||
if ( values.size() < 2 ) continue;
|
||||
|
||||
if ( settings.topUpper() < values.front() )
|
||||
{
|
||||
i.front().push_back( settings.topUpper() );
|
||||
i.front().push_back( values[0 + 1] );
|
||||
}
|
||||
|
||||
for ( const auto& value : values )
|
||||
{
|
||||
i.front().push_back( value );
|
||||
}
|
||||
|
||||
i.front().push_back( settings.bottomLower() );
|
||||
i.front().push_back( values.back() );
|
||||
if ( settings.bottomLower() > values[values.size() - 2] )
|
||||
{
|
||||
i.front().push_back( settings.bottomLower() );
|
||||
i.front().push_back( values.back() );
|
||||
}
|
||||
|
||||
newPropertyVD.addRecord( Opm::DeckRecord{ std::move( i ) } );
|
||||
}
|
||||
@@ -823,9 +834,12 @@ void extendDepthTable( const RigModelPaddingSettings& settings, Opm::DeckKeyword
|
||||
newPropertyVD.addRecord( std::move( first ) );
|
||||
}
|
||||
|
||||
propertyVD = newPropertyVD;
|
||||
propertyVD = std::move( newPropertyVD );
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Extend EQUIL + depth tables in SOLUTION section (padmodel lines 869-886)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -852,7 +866,7 @@ void extendSolution( const RigModelPaddingSettings& settings, Opm::FileDeck& fil
|
||||
else if ( keyword.name() == "RSVD" || keyword.name() == "RVVD" || keyword.name() == "RTEMPVD" || keyword.name() == "PBVD" ||
|
||||
keyword.name() == "PDVD" )
|
||||
{
|
||||
extendDepthTable( settings, const_cast<Opm::DeckKeyword&>( keyword ) );
|
||||
RigPadModel::extendDepthTable( settings, const_cast<Opm::DeckKeyword&>( keyword ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
namespace Opm
|
||||
{
|
||||
class Deck;
|
||||
class DeckKeyword;
|
||||
class FileDeck;
|
||||
} // namespace Opm
|
||||
|
||||
@@ -58,4 +59,6 @@ public:
|
||||
int nzLower,
|
||||
double upperDefault,
|
||||
double lowerDefault );
|
||||
|
||||
static void extendDepthTable( const RigModelPaddingSettings& settings, Opm::DeckKeyword& propertyVD );
|
||||
};
|
||||
|
||||
@@ -18,9 +18,11 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "RigModelPaddingSettings.h"
|
||||
#include "RigPadModel.h"
|
||||
|
||||
#include "opm/input/eclipse/Deck/Deck.hpp"
|
||||
#include "opm/input/eclipse/Deck/DeckKeyword.hpp"
|
||||
#include "opm/input/eclipse/Parser/Parser.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -309,3 +311,196 @@ SCHEDULE
|
||||
// Unknown keyword should return 0.0
|
||||
EXPECT_DOUBLE_EQ( 0.0, RigPadModel::getPropsDefaultValue( deck, "UNKNOWN_KEYWORD" ) );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Helper: parse a minimal deck with RSVD and return a mutable copy of the keyword
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
static Opm::DeckKeyword createRsvdKeyword( const std::string& rsvdData )
|
||||
{
|
||||
std::string deckString = R"(
|
||||
EQLDIMS
|
||||
1 /
|
||||
SOLUTION
|
||||
RSVD
|
||||
)" + rsvdData;
|
||||
|
||||
auto deck = Opm::Parser{}.parseString( deckString );
|
||||
return deck["RSVD"][0];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Helper: extract depth values (every other element starting at index 0) from RSVD record
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
static std::vector<double> getDepths( const Opm::DeckKeyword& kw, size_t recordIndex = 0 )
|
||||
{
|
||||
const auto& values = kw.getRecord( recordIndex ).getItem( 0 ).getData<double>();
|
||||
std::vector<double> depths;
|
||||
for ( size_t i = 0; i < values.size(); i += 2 )
|
||||
{
|
||||
depths.push_back( values[i] );
|
||||
}
|
||||
return depths;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Helper: verify strict monotonicity of depth values in an RSVD record
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
static void verifyMonotonicDepths( const Opm::DeckKeyword& kw, size_t recordIndex = 0 )
|
||||
{
|
||||
auto depths = getDepths( kw, recordIndex );
|
||||
for ( size_t i = 1; i < depths.size(); ++i )
|
||||
{
|
||||
EXPECT_LT( depths[i - 1], depths[i] ) << "Depth values must be strictly monotonic at index " << i;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// extendDepthTable: both top and bottom rows are added when table doesn't cover padding range
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigPadModel, ExtendDepthTable_BothExtended )
|
||||
{
|
||||
// RSVD table: depths 2000, 2500, 3000 with Rs values 120, 110, 100
|
||||
auto rsvd = createRsvdKeyword( " 2000.0 120.0\n 2500.0 110.0\n 3000.0 100.0 /\n" );
|
||||
|
||||
RigModelPaddingSettings settings;
|
||||
settings.setTopUpper( 1500.0 );
|
||||
settings.setBottomLower( 3500.0 );
|
||||
|
||||
RigPadModel::extendDepthTable( settings, rsvd );
|
||||
|
||||
// The function duplicates the first record for the padding equilibrium region
|
||||
ASSERT_EQ( 2u, rsvd.size() );
|
||||
|
||||
const auto& values = rsvd.getRecord( 0 ).getItem( 0 ).getData<double>();
|
||||
|
||||
// 5 depth-property pairs: prepended + 3 original + appended
|
||||
ASSERT_EQ( 10u, values.size() );
|
||||
|
||||
// Prepended row: depth=1500, property copied from first entry (120)
|
||||
EXPECT_DOUBLE_EQ( 1500.0, values[0] );
|
||||
EXPECT_DOUBLE_EQ( 120.0, values[1] );
|
||||
|
||||
// Original data preserved
|
||||
EXPECT_DOUBLE_EQ( 2000.0, values[2] );
|
||||
EXPECT_DOUBLE_EQ( 120.0, values[3] );
|
||||
EXPECT_DOUBLE_EQ( 2500.0, values[4] );
|
||||
EXPECT_DOUBLE_EQ( 110.0, values[5] );
|
||||
EXPECT_DOUBLE_EQ( 3000.0, values[6] );
|
||||
EXPECT_DOUBLE_EQ( 100.0, values[7] );
|
||||
|
||||
// Appended row: depth=3500, property copied from last entry (100)
|
||||
EXPECT_DOUBLE_EQ( 3500.0, values[8] );
|
||||
EXPECT_DOUBLE_EQ( 100.0, values[9] );
|
||||
|
||||
verifyMonotonicDepths( rsvd );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// extendDepthTable: table already covers top, only bottom row added
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigPadModel, ExtendDepthTable_TopAlreadyCovered )
|
||||
{
|
||||
auto rsvd = createRsvdKeyword( " 2000.0 120.0\n 2500.0 110.0\n 3000.0 100.0 /\n" );
|
||||
|
||||
RigModelPaddingSettings settings;
|
||||
settings.setTopUpper( 2500.0 ); // >= first depth (2000), so no top row
|
||||
settings.setBottomLower( 3500.0 );
|
||||
|
||||
RigPadModel::extendDepthTable( settings, rsvd );
|
||||
|
||||
const auto& values = rsvd.getRecord( 0 ).getItem( 0 ).getData<double>();
|
||||
|
||||
// 4 depth-property pairs: 3 original + 1 appended
|
||||
ASSERT_EQ( 8u, values.size() );
|
||||
|
||||
// No prepended row - starts with original data
|
||||
EXPECT_DOUBLE_EQ( 2000.0, values[0] );
|
||||
EXPECT_DOUBLE_EQ( 120.0, values[1] );
|
||||
|
||||
// Appended row
|
||||
EXPECT_DOUBLE_EQ( 3500.0, values[6] );
|
||||
EXPECT_DOUBLE_EQ( 100.0, values[7] );
|
||||
|
||||
verifyMonotonicDepths( rsvd );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// extendDepthTable: table already covers bottom, only top row added
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigPadModel, ExtendDepthTable_BottomAlreadyCovered )
|
||||
{
|
||||
auto rsvd = createRsvdKeyword( " 2000.0 120.0\n 2500.0 110.0\n 3000.0 100.0 /\n" );
|
||||
|
||||
RigModelPaddingSettings settings;
|
||||
settings.setTopUpper( 1500.0 );
|
||||
settings.setBottomLower( 2800.0 ); // < last depth (3000), so no bottom row
|
||||
|
||||
RigPadModel::extendDepthTable( settings, rsvd );
|
||||
|
||||
const auto& values = rsvd.getRecord( 0 ).getItem( 0 ).getData<double>();
|
||||
|
||||
// 4 depth-property pairs: 1 prepended + 3 original
|
||||
ASSERT_EQ( 8u, values.size() );
|
||||
|
||||
// Prepended row
|
||||
EXPECT_DOUBLE_EQ( 1500.0, values[0] );
|
||||
EXPECT_DOUBLE_EQ( 120.0, values[1] );
|
||||
|
||||
// No appended row - ends with original data
|
||||
EXPECT_DOUBLE_EQ( 3000.0, values[6] );
|
||||
EXPECT_DOUBLE_EQ( 100.0, values[7] );
|
||||
|
||||
verifyMonotonicDepths( rsvd );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// extendDepthTable: table already covers both top and bottom, no rows added
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigPadModel, ExtendDepthTable_BothAlreadyCovered )
|
||||
{
|
||||
auto rsvd = createRsvdKeyword( " 2000.0 120.0\n 2500.0 110.0\n 3000.0 100.0 /\n" );
|
||||
|
||||
RigModelPaddingSettings settings;
|
||||
settings.setTopUpper( 2200.0 ); // > first depth (2000)
|
||||
settings.setBottomLower( 2800.0 ); // < last depth (3000)
|
||||
|
||||
RigPadModel::extendDepthTable( settings, rsvd );
|
||||
|
||||
const auto& values = rsvd.getRecord( 0 ).getItem( 0 ).getData<double>();
|
||||
|
||||
// Unchanged: 3 original depth-property pairs
|
||||
ASSERT_EQ( 6u, values.size() );
|
||||
|
||||
EXPECT_DOUBLE_EQ( 2000.0, values[0] );
|
||||
EXPECT_DOUBLE_EQ( 120.0, values[1] );
|
||||
EXPECT_DOUBLE_EQ( 2500.0, values[2] );
|
||||
EXPECT_DOUBLE_EQ( 110.0, values[3] );
|
||||
EXPECT_DOUBLE_EQ( 3000.0, values[4] );
|
||||
EXPECT_DOUBLE_EQ( 100.0, values[5] );
|
||||
|
||||
verifyMonotonicDepths( rsvd );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// extendDepthTable: exact boundary match - strict inequalities prevent duplicate depths
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigPadModel, ExtendDepthTable_ExactBoundaryMatch )
|
||||
{
|
||||
auto rsvd = createRsvdKeyword( " 2000.0 120.0\n 2500.0 110.0\n 3000.0 100.0 /\n" );
|
||||
|
||||
RigModelPaddingSettings settings;
|
||||
settings.setTopUpper( 2000.0 ); // == first depth, strict < means no prepend
|
||||
settings.setBottomLower( 3000.0 ); // == last depth, strict > means no append
|
||||
|
||||
RigPadModel::extendDepthTable( settings, rsvd );
|
||||
|
||||
const auto& values = rsvd.getRecord( 0 ).getItem( 0 ).getData<double>();
|
||||
|
||||
// Unchanged: 3 original depth-property pairs, no duplicates
|
||||
ASSERT_EQ( 6u, values.size() );
|
||||
|
||||
EXPECT_DOUBLE_EQ( 2000.0, values[0] );
|
||||
EXPECT_DOUBLE_EQ( 3000.0, values[4] );
|
||||
|
||||
verifyMonotonicDepths( rsvd );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user