Skip to content

Commit a184902

Browse files
kribenvkip
andcommitted
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>
1 parent 68b193c commit a184902

3 files changed

Lines changed: 212 additions & 6 deletions

File tree

ApplicationLibCode/ReservoirDataModel/SimulationFile/RigPadModel.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -788,10 +788,12 @@ void extendEQUIL( Opm::DeckKeyword& equil )
788788
equil.addRecord( std::move( record ) );
789789
}
790790

791+
} // namespace
792+
791793
//--------------------------------------------------------------------------------------------------
792794
/// Extend RSVD/RVVD/RTEMPVD/PBVD/PDVD tables (padmodel lines 832-867)
793795
//--------------------------------------------------------------------------------------------------
794-
void extendDepthTable( const RigModelPaddingSettings& settings, Opm::DeckKeyword& propertyVD )
796+
void RigPadModel::extendDepthTable( const RigModelPaddingSettings& settings, Opm::DeckKeyword& propertyVD )
795797
{
796798
assert( !propertyVD.empty() );
797799

@@ -804,16 +806,22 @@ void extendDepthTable( const RigModelPaddingSettings& settings, Opm::DeckKeyword
804806

805807
const auto& values = table.getItem( 0 ).getData<double>();
806808

807-
i.front().push_back( settings.topUpper() );
808-
i.front().push_back( values[0 + 1] );
809+
if ( settings.topUpper() < values.front() )
810+
{
811+
i.front().push_back( settings.topUpper() );
812+
i.front().push_back( values[0 + 1] );
813+
}
809814

810815
for ( const auto& value : values )
811816
{
812817
i.front().push_back( value );
813818
}
814819

815-
i.front().push_back( settings.bottomLower() );
816-
i.front().push_back( values.back() );
820+
if ( settings.bottomLower() > values[values.size() - 2] )
821+
{
822+
i.front().push_back( settings.bottomLower() );
823+
i.front().push_back( values.back() );
824+
}
817825

818826
newPropertyVD.addRecord( Opm::DeckRecord{ std::move( i ) } );
819827
}
@@ -826,6 +834,9 @@ void extendDepthTable( const RigModelPaddingSettings& settings, Opm::DeckKeyword
826834
propertyVD = newPropertyVD;
827835
}
828836

837+
namespace
838+
{
839+
829840
//--------------------------------------------------------------------------------------------------
830841
/// Extend EQUIL + depth tables in SOLUTION section (padmodel lines 869-886)
831842
//--------------------------------------------------------------------------------------------------
@@ -852,7 +863,7 @@ void extendSolution( const RigModelPaddingSettings& settings, Opm::FileDeck& fil
852863
else if ( keyword.name() == "RSVD" || keyword.name() == "RVVD" || keyword.name() == "RTEMPVD" || keyword.name() == "PBVD" ||
853864
keyword.name() == "PDVD" )
854865
{
855-
extendDepthTable( settings, const_cast<Opm::DeckKeyword&>( keyword ) );
866+
RigPadModel::extendDepthTable( settings, const_cast<Opm::DeckKeyword&>( keyword ) );
856867
}
857868
}
858869
}

ApplicationLibCode/ReservoirDataModel/SimulationFile/RigPadModel.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
namespace Opm
2828
{
2929
class Deck;
30+
class DeckKeyword;
3031
class FileDeck;
3132
} // namespace Opm
3233

@@ -58,4 +59,6 @@ class RigPadModel
5859
int nzLower,
5960
double upperDefault,
6061
double lowerDefault );
62+
63+
static void extendDepthTable( const RigModelPaddingSettings& settings, Opm::DeckKeyword& propertyVD );
6164
};

ApplicationLibCode/UnitTests/RigPadModel-Test.cpp

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818

1919
#include "gtest/gtest.h"
2020

21+
#include "RigModelPaddingSettings.h"
2122
#include "RigPadModel.h"
2223

2324
#include "opm/input/eclipse/Deck/Deck.hpp"
25+
#include "opm/input/eclipse/Deck/DeckKeyword.hpp"
2426
#include "opm/input/eclipse/Parser/Parser.hpp"
2527

2628
#include <algorithm>
@@ -309,3 +311,193 @@ SCHEDULE
309311
// Unknown keyword should return 0.0
310312
EXPECT_DOUBLE_EQ( 0.0, RigPadModel::getPropsDefaultValue( deck, "UNKNOWN_KEYWORD" ) );
311313
}
314+
315+
//--------------------------------------------------------------------------------------------------
316+
// Helper: parse a minimal deck with RSVD and return a mutable copy of the keyword
317+
//--------------------------------------------------------------------------------------------------
318+
static Opm::DeckKeyword createRsvdKeyword( const std::string& rsvdData )
319+
{
320+
std::string deckString = R"(
321+
EQLDIMS
322+
1 /
323+
SOLUTION
324+
RSVD
325+
)" + rsvdData;
326+
327+
auto deck = Opm::Parser{}.parseString( deckString );
328+
return deck["RSVD"][0];
329+
}
330+
331+
//--------------------------------------------------------------------------------------------------
332+
// Helper: extract depth values (every other element starting at index 0) from RSVD record
333+
//--------------------------------------------------------------------------------------------------
334+
static std::vector<double> getDepths( const Opm::DeckKeyword& kw, size_t recordIndex = 0 )
335+
{
336+
const auto& values = kw.getRecord( recordIndex ).getItem( 0 ).getData<double>();
337+
std::vector<double> depths;
338+
for ( size_t i = 0; i < values.size(); i += 2 )
339+
{
340+
depths.push_back( values[i] );
341+
}
342+
return depths;
343+
}
344+
345+
//--------------------------------------------------------------------------------------------------
346+
// Helper: verify strict monotonicity of depth values in an RSVD record
347+
//--------------------------------------------------------------------------------------------------
348+
static void verifyMonotonicDepths( const Opm::DeckKeyword& kw, size_t recordIndex = 0 )
349+
{
350+
auto depths = getDepths( kw, recordIndex );
351+
for ( size_t i = 1; i < depths.size(); ++i )
352+
{
353+
EXPECT_LT( depths[i - 1], depths[i] ) << "Depth values must be strictly monotonic at index " << i;
354+
}
355+
}
356+
357+
//--------------------------------------------------------------------------------------------------
358+
/// extendDepthTable: both top and bottom rows are added when table doesn't cover padding range
359+
//--------------------------------------------------------------------------------------------------
360+
TEST( RigPadModel, ExtendDepthTable_BothExtended )
361+
{
362+
// RSVD table: depths 2000, 2500, 3000 with Rs values 120, 110, 100
363+
auto rsvd = createRsvdKeyword( " 2000.0 120.0\n 2500.0 110.0\n 3000.0 100.0 /\n" );
364+
365+
RigModelPaddingSettings settings;
366+
settings.setTopUpper( 1500.0 );
367+
settings.setBottomLower( 3500.0 );
368+
369+
RigPadModel::extendDepthTable( settings, rsvd );
370+
371+
const auto& values = rsvd.getRecord( 0 ).getItem( 0 ).getData<double>();
372+
373+
// 5 depth-property pairs: prepended + 3 original + appended
374+
ASSERT_EQ( 10u, values.size() );
375+
376+
// Prepended row: depth=1500, property copied from first entry (120)
377+
EXPECT_DOUBLE_EQ( 1500.0, values[0] );
378+
EXPECT_DOUBLE_EQ( 120.0, values[1] );
379+
380+
// Original data preserved
381+
EXPECT_DOUBLE_EQ( 2000.0, values[2] );
382+
EXPECT_DOUBLE_EQ( 120.0, values[3] );
383+
EXPECT_DOUBLE_EQ( 2500.0, values[4] );
384+
EXPECT_DOUBLE_EQ( 110.0, values[5] );
385+
EXPECT_DOUBLE_EQ( 3000.0, values[6] );
386+
EXPECT_DOUBLE_EQ( 100.0, values[7] );
387+
388+
// Appended row: depth=3500, property copied from last entry (100)
389+
EXPECT_DOUBLE_EQ( 3500.0, values[8] );
390+
EXPECT_DOUBLE_EQ( 100.0, values[9] );
391+
392+
verifyMonotonicDepths( rsvd );
393+
}
394+
395+
//--------------------------------------------------------------------------------------------------
396+
/// extendDepthTable: table already covers top, only bottom row added
397+
//--------------------------------------------------------------------------------------------------
398+
TEST( RigPadModel, ExtendDepthTable_TopAlreadyCovered )
399+
{
400+
auto rsvd = createRsvdKeyword( " 2000.0 120.0\n 2500.0 110.0\n 3000.0 100.0 /\n" );
401+
402+
RigModelPaddingSettings settings;
403+
settings.setTopUpper( 2500.0 ); // >= first depth (2000), so no top row
404+
settings.setBottomLower( 3500.0 );
405+
406+
RigPadModel::extendDepthTable( settings, rsvd );
407+
408+
const auto& values = rsvd.getRecord( 0 ).getItem( 0 ).getData<double>();
409+
410+
// 4 depth-property pairs: 3 original + 1 appended
411+
ASSERT_EQ( 8u, values.size() );
412+
413+
// No prepended row - starts with original data
414+
EXPECT_DOUBLE_EQ( 2000.0, values[0] );
415+
EXPECT_DOUBLE_EQ( 120.0, values[1] );
416+
417+
// Appended row
418+
EXPECT_DOUBLE_EQ( 3500.0, values[6] );
419+
EXPECT_DOUBLE_EQ( 100.0, values[7] );
420+
421+
verifyMonotonicDepths( rsvd );
422+
}
423+
424+
//--------------------------------------------------------------------------------------------------
425+
/// extendDepthTable: table already covers bottom, only top row added
426+
//--------------------------------------------------------------------------------------------------
427+
TEST( RigPadModel, ExtendDepthTable_BottomAlreadyCovered )
428+
{
429+
auto rsvd = createRsvdKeyword( " 2000.0 120.0\n 2500.0 110.0\n 3000.0 100.0 /\n" );
430+
431+
RigModelPaddingSettings settings;
432+
settings.setTopUpper( 1500.0 );
433+
settings.setBottomLower( 2800.0 ); // < last depth (3000), so no bottom row
434+
435+
RigPadModel::extendDepthTable( settings, rsvd );
436+
437+
const auto& values = rsvd.getRecord( 0 ).getItem( 0 ).getData<double>();
438+
439+
// 4 depth-property pairs: 1 prepended + 3 original
440+
ASSERT_EQ( 8u, values.size() );
441+
442+
// Prepended row
443+
EXPECT_DOUBLE_EQ( 1500.0, values[0] );
444+
EXPECT_DOUBLE_EQ( 120.0, values[1] );
445+
446+
// No appended row - ends with original data
447+
EXPECT_DOUBLE_EQ( 3000.0, values[6] );
448+
EXPECT_DOUBLE_EQ( 100.0, values[7] );
449+
450+
verifyMonotonicDepths( rsvd );
451+
}
452+
453+
//--------------------------------------------------------------------------------------------------
454+
/// extendDepthTable: table already covers both top and bottom, no rows added
455+
//--------------------------------------------------------------------------------------------------
456+
TEST( RigPadModel, ExtendDepthTable_BothAlreadyCovered )
457+
{
458+
auto rsvd = createRsvdKeyword( " 2000.0 120.0\n 2500.0 110.0\n 3000.0 100.0 /\n" );
459+
460+
RigModelPaddingSettings settings;
461+
settings.setTopUpper( 2200.0 ); // > first depth (2000)
462+
settings.setBottomLower( 2800.0 ); // < last depth (3000)
463+
464+
RigPadModel::extendDepthTable( settings, rsvd );
465+
466+
const auto& values = rsvd.getRecord( 0 ).getItem( 0 ).getData<double>();
467+
468+
// Unchanged: 3 original depth-property pairs
469+
ASSERT_EQ( 6u, values.size() );
470+
471+
EXPECT_DOUBLE_EQ( 2000.0, values[0] );
472+
EXPECT_DOUBLE_EQ( 120.0, values[1] );
473+
EXPECT_DOUBLE_EQ( 2500.0, values[2] );
474+
EXPECT_DOUBLE_EQ( 110.0, values[3] );
475+
EXPECT_DOUBLE_EQ( 3000.0, values[4] );
476+
EXPECT_DOUBLE_EQ( 100.0, values[5] );
477+
478+
verifyMonotonicDepths( rsvd );
479+
}
480+
481+
//--------------------------------------------------------------------------------------------------
482+
/// extendDepthTable: exact boundary match - strict inequalities prevent duplicate depths
483+
//--------------------------------------------------------------------------------------------------
484+
TEST( RigPadModel, ExtendDepthTable_ExactBoundaryMatch )
485+
{
486+
auto rsvd = createRsvdKeyword( " 2000.0 120.0\n 2500.0 110.0\n 3000.0 100.0 /\n" );
487+
488+
RigModelPaddingSettings settings;
489+
settings.setTopUpper( 2000.0 ); // == first depth, strict < means no prepend
490+
settings.setBottomLower( 3000.0 ); // == last depth, strict > means no append
491+
492+
RigPadModel::extendDepthTable( settings, rsvd );
493+
494+
const auto& values = rsvd.getRecord( 0 ).getItem( 0 ).getData<double>();
495+
496+
// Unchanged: 3 original depth-property pairs, no duplicates
497+
ASSERT_EQ( 6u, values.size() );
498+
499+
EXPECT_DOUBLE_EQ( 2000.0, values[0] );
500+
EXPECT_DOUBLE_EQ( 3000.0, values[4] );
501+
502+
verifyMonotonicDepths( rsvd );
503+
}

0 commit comments

Comments
 (0)