From c156d5d23c3198dce5c81e55c5deae137081dda4 Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Mon, 16 Feb 2026 14:28:58 +0100 Subject: [PATCH] #13606 Summary Calculator: Add support for block vectors --- .../RimDataSourceSteppingTools.cpp | 1 + .../RimSummaryCalculation.cpp | 60 ++++- .../ProjectDataModel/RimSummaryCalculation.h | 4 +- ApplicationLibCode/UnitTests/CMakeLists.txt | 1 + .../UnitTests/RimSummaryCalculation-Test.cpp | 219 ++++++++++++++++++ 5 files changed, 282 insertions(+), 3 deletions(-) create mode 100644 ApplicationLibCode/UnitTests/RimSummaryCalculation-Test.cpp diff --git a/ApplicationLibCode/ProjectDataModel/RimDataSourceSteppingTools.cpp b/ApplicationLibCode/ProjectDataModel/RimDataSourceSteppingTools.cpp index aedf927fbd..2f411bae98 100644 --- a/ApplicationLibCode/ProjectDataModel/RimDataSourceSteppingTools.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimDataSourceSteppingTools.cpp @@ -150,6 +150,7 @@ bool RimDataSourceSteppingTools::updateAddressIfMatching( const QVariant& } } else if ( category == RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_BLOCK || + category == RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_BLOCK_LGR || category == RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_WELL_CONNECTION ) { std::string oldString = oldValue.toString().toStdString(); diff --git a/ApplicationLibCode/ProjectDataModel/RimSummaryCalculation.cpp b/ApplicationLibCode/ProjectDataModel/RimSummaryCalculation.cpp index 174a20222b..166fca3bb1 100644 --- a/ApplicationLibCode/ProjectDataModel/RimSummaryCalculation.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimSummaryCalculation.cpp @@ -262,7 +262,8 @@ void RimSummaryCalculation::substituteVariables( std::vector } } } + else if ( category == RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_BLOCK ) + { + std::set> uniqueBlocks; + std::for_each( allResultAddresses.begin(), + allResultAddresses.end(), + [&]( const auto& addr ) + { + if ( addr.category() == SummaryCategory::SUMMARY_BLOCK ) + { + uniqueBlocks.insert( { addr.cellI(), addr.cellJ(), addr.cellK() } ); + } + } ); + + for ( auto block : uniqueBlocks ) + { + addresses.push_back( + RimSummaryCalculationAddress( RifEclipseSummaryAddress::blockAddress( name, block[0], block[1], block[2], m_id ) ) ); + } + } + else if ( category == RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_BLOCK_LGR ) + { + std::set uniqueLgrNames; + std::for_each( allResultAddresses.begin(), + allResultAddresses.end(), + [&]( const auto& addr ) + { + if ( addr.category() == SummaryCategory::SUMMARY_BLOCK_LGR ) uniqueLgrNames.insert( addr.lgrName() ); + } ); + + for ( auto lgrName : uniqueLgrNames ) + { + std::set> uniqueBlocks; + std::for_each( allResultAddresses.begin(), + allResultAddresses.end(), + [&]( const auto& addr ) + { + if ( addr.category() == SummaryCategory::SUMMARY_BLOCK_LGR && addr.lgrName() == lgrName ) + { + uniqueBlocks.insert( { addr.cellI(), addr.cellJ(), addr.cellK() } ); + } + } ); + + for ( auto block : uniqueBlocks ) + { + addresses.push_back( RimSummaryCalculationAddress( + RifEclipseSummaryAddress::blockLgrAddress( name, lgrName, block[0], block[1], block[2], m_id ) ) ); + } + } + } else if ( category == RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_IMPORTED ) { addresses.push_back( RimSummaryCalculationAddress( RifEclipseSummaryAddress::importedAddress( name, m_id ) ) ); @@ -697,6 +747,14 @@ RimSummaryCalculationAddress RimSummaryCalculation::singleAddressesForCategory( { return RifEclipseSummaryAddress::wellConnectionAddress( name, address.wellName(), address.cellI(), address.cellJ(), address.cellK(), m_id ); } + else if ( category == RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_BLOCK ) + { + return RifEclipseSummaryAddress::blockAddress( name, address.cellI(), address.cellJ(), address.cellK(), m_id ); + } + else if ( category == RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_BLOCK_LGR ) + { + return RifEclipseSummaryAddress::blockLgrAddress( name, address.lgrName(), address.cellI(), address.cellJ(), address.cellK(), m_id ); + } else if ( category == RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_IMPORTED ) { return RifEclipseSummaryAddress::importedAddress( name, m_id ); diff --git a/ApplicationLibCode/ProjectDataModel/RimSummaryCalculation.h b/ApplicationLibCode/ProjectDataModel/RimSummaryCalculation.h index dac018ca50..dafee66ed8 100644 --- a/ApplicationLibCode/ProjectDataModel/RimSummaryCalculation.h +++ b/ApplicationLibCode/ProjectDataModel/RimSummaryCalculation.h @@ -72,6 +72,8 @@ public: bool isDistributeToOtherItems() const; bool isDistributeToAllCases() const; + static void substituteVariables( std::vector& vars, const RifEclipseSummaryAddress& address ); + protected: RimSummaryCalculationVariable* createVariable() override; @@ -83,8 +85,6 @@ protected: std::optional, std::vector>> calculateWithSubstitutions( RimSummaryCase* summaryCase, const RifEclipseSummaryAddress& addr ); - static void substituteVariables( std::vector& vars, const RifEclipseSummaryAddress& address ); - std::vector allAddressesForCategory( RifEclipseSummaryAddressDefines::SummaryCategory category, const std::set& allResultAddresses ) const; diff --git a/ApplicationLibCode/UnitTests/CMakeLists.txt b/ApplicationLibCode/UnitTests/CMakeLists.txt index 14a85844f6..88711bbe60 100644 --- a/ApplicationLibCode/UnitTests/CMakeLists.txt +++ b/ApplicationLibCode/UnitTests/CMakeLists.txt @@ -128,6 +128,7 @@ set(SOURCE_UNITTEST_FILES ${CMAKE_CURRENT_LIST_DIR}/RiaSummaryAddressCollectionTools-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RimMockSummaryCase.h ${CMAKE_CURRENT_LIST_DIR}/RimMockSummaryCase-Test.cpp + ${CMAKE_CURRENT_LIST_DIR}/RimSummaryCalculation-Test.cpp ) if(RESINSIGHT_ENABLE_GRPC) diff --git a/ApplicationLibCode/UnitTests/RimSummaryCalculation-Test.cpp b/ApplicationLibCode/UnitTests/RimSummaryCalculation-Test.cpp new file mode 100644 index 0000000000..a4dc946f40 --- /dev/null +++ b/ApplicationLibCode/UnitTests/RimSummaryCalculation-Test.cpp @@ -0,0 +1,219 @@ +#include "gtest/gtest.h" + +#include "RifEclipseSummaryAddress.h" +#include "RifEclipseSummaryAddressDefines.h" +#include "RimSummaryCalculation.h" + +using SummaryCategory = RifEclipseSummaryAddressDefines::SummaryCategory; + +//-------------------------------------------------------------------------------------------------- +/// Test subclass that exposes protected methods for unit testing +//-------------------------------------------------------------------------------------------------- +class RimSummaryCalculationTester : public RimSummaryCalculation +{ +public: + RimSummaryCalculationTester() + { + setExpression( "CALC := a + b" ); + setId( 42 ); + } + + std::vector testAllAddressesForCategory( SummaryCategory category, + const std::set& allResultAddresses ) const + { + return allAddressesForCategory( category, allResultAddresses ); + } + + RimSummaryCalculationAddress testSingleAddressesForCategory( const RifEclipseSummaryAddress& address ) const + { + return singleAddressesForCategory( address ); + } +}; + +//-------------------------------------------------------------------------------------------------- +// allAddressesForCategory - SUMMARY_BLOCK +//-------------------------------------------------------------------------------------------------- + +TEST( RimSummaryCalculation, Block_UniqueBlocks ) +{ + RimSummaryCalculationTester calc; + + std::set addresses; + addresses.insert( RifEclipseSummaryAddress::blockAddress( "BPR", 1, 2, 3 ) ); + addresses.insert( RifEclipseSummaryAddress::blockAddress( "BPR", 1, 2, 3 ) ); // duplicate + addresses.insert( RifEclipseSummaryAddress::blockAddress( "BPR", 4, 5, 6 ) ); + addresses.insert( RifEclipseSummaryAddress::blockAddress( "BPR", 7, 8, 9 ) ); + + auto result = calc.testAllAddressesForCategory( SummaryCategory::SUMMARY_BLOCK, addresses ); + + EXPECT_EQ( 3u, result.size() ); +} + +TEST( RimSummaryCalculation, Block_EmptyInput ) +{ + RimSummaryCalculationTester calc; + + std::set addresses; + + auto result = calc.testAllAddressesForCategory( SummaryCategory::SUMMARY_BLOCK, addresses ); + + EXPECT_TRUE( result.empty() ); +} + +TEST( RimSummaryCalculation, Block_IgnoresOtherCategories ) +{ + RimSummaryCalculationTester calc; + + std::set addresses; + addresses.insert( RifEclipseSummaryAddress::wellAddress( "WOPR", "W1" ) ); + addresses.insert( RifEclipseSummaryAddress::wellAddress( "WOPR", "W2" ) ); + + auto result = calc.testAllAddressesForCategory( SummaryCategory::SUMMARY_BLOCK, addresses ); + + EXPECT_TRUE( result.empty() ); +} + +TEST( RimSummaryCalculation, Block_SingleBlock ) +{ + RimSummaryCalculationTester calc; + + std::set addresses; + addresses.insert( RifEclipseSummaryAddress::blockAddress( "BPR", 10, 20, 30 ) ); + + auto result = calc.testAllAddressesForCategory( SummaryCategory::SUMMARY_BLOCK, addresses ); + + ASSERT_EQ( 1u, result.size() ); + EXPECT_EQ( 10, result[0].address().cellI() ); + EXPECT_EQ( 20, result[0].address().cellJ() ); + EXPECT_EQ( 30, result[0].address().cellK() ); + EXPECT_EQ( "CALC", result[0].address().vectorName() ); + EXPECT_EQ( 42, result[0].address().id() ); +} + +//-------------------------------------------------------------------------------------------------- +// allAddressesForCategory - SUMMARY_BLOCK_LGR +//-------------------------------------------------------------------------------------------------- + +TEST( RimSummaryCalculation, BlockLgr_MultiLgrMultiBlock ) +{ + RimSummaryCalculationTester calc; + + std::set addresses; + addresses.insert( RifEclipseSummaryAddress::blockLgrAddress( "LBPR", "LGR1", 1, 1, 1 ) ); + addresses.insert( RifEclipseSummaryAddress::blockLgrAddress( "LBPR", "LGR1", 2, 2, 2 ) ); + addresses.insert( RifEclipseSummaryAddress::blockLgrAddress( "LBPR", "LGR2", 3, 3, 3 ) ); + addresses.insert( RifEclipseSummaryAddress::blockLgrAddress( "LBPR", "LGR2", 4, 4, 4 ) ); + + auto result = calc.testAllAddressesForCategory( SummaryCategory::SUMMARY_BLOCK_LGR, addresses ); + + EXPECT_EQ( 4u, result.size() ); +} + +TEST( RimSummaryCalculation, BlockLgr_DeduplicatesWithinLgr ) +{ + RimSummaryCalculationTester calc; + + std::set addresses; + addresses.insert( RifEclipseSummaryAddress::blockLgrAddress( "LBPR", "LGR1", 1, 1, 1 ) ); + addresses.insert( RifEclipseSummaryAddress::blockLgrAddress( "LBPR", "LGR1", 1, 1, 1 ) ); // duplicate + addresses.insert( RifEclipseSummaryAddress::blockLgrAddress( "LBPR", "LGR1", 2, 2, 2 ) ); + + auto result = calc.testAllAddressesForCategory( SummaryCategory::SUMMARY_BLOCK_LGR, addresses ); + + EXPECT_EQ( 2u, result.size() ); +} + +TEST( RimSummaryCalculation, BlockLgr_IgnoresOtherCategories ) +{ + RimSummaryCalculationTester calc; + + std::set addresses; + addresses.insert( RifEclipseSummaryAddress::blockAddress( "BPR", 1, 2, 3 ) ); + addresses.insert( RifEclipseSummaryAddress::wellAddress( "WOPR", "W1" ) ); + addresses.insert( RifEclipseSummaryAddress::blockLgrAddress( "LBPR", "LGR1", 5, 6, 7 ) ); + + auto result = calc.testAllAddressesForCategory( SummaryCategory::SUMMARY_BLOCK_LGR, addresses ); + + ASSERT_EQ( 1u, result.size() ); + EXPECT_EQ( "LGR1", result[0].address().lgrName() ); + EXPECT_EQ( 5, result[0].address().cellI() ); + EXPECT_EQ( 6, result[0].address().cellJ() ); + EXPECT_EQ( 7, result[0].address().cellK() ); +} + +//-------------------------------------------------------------------------------------------------- +// singleAddressesForCategory +//-------------------------------------------------------------------------------------------------- + +TEST( RimSummaryCalculation, SingleAddress_Block ) +{ + RimSummaryCalculationTester calc; + + auto input = RifEclipseSummaryAddress::blockAddress( "BPR", 10, 20, 30 ); + auto result = calc.testSingleAddressesForCategory( input ); + + EXPECT_EQ( "CALC", result.address().vectorName() ); + EXPECT_EQ( 42, result.address().id() ); + EXPECT_EQ( 10, result.address().cellI() ); + EXPECT_EQ( 20, result.address().cellJ() ); + EXPECT_EQ( 30, result.address().cellK() ); + EXPECT_EQ( SummaryCategory::SUMMARY_BLOCK, result.address().category() ); +} + +TEST( RimSummaryCalculation, SingleAddress_BlockLgr ) +{ + RimSummaryCalculationTester calc; + + auto input = RifEclipseSummaryAddress::blockLgrAddress( "LBPR", "LGR1", 5, 6, 7 ); + auto result = calc.testSingleAddressesForCategory( input ); + + EXPECT_EQ( "CALC", result.address().vectorName() ); + EXPECT_EQ( 42, result.address().id() ); + EXPECT_EQ( "LGR1", result.address().lgrName() ); + EXPECT_EQ( 5, result.address().cellI() ); + EXPECT_EQ( 6, result.address().cellJ() ); + EXPECT_EQ( 7, result.address().cellK() ); + EXPECT_EQ( SummaryCategory::SUMMARY_BLOCK_LGR, result.address().category() ); +} + +//-------------------------------------------------------------------------------------------------- +// substituteVariables +//-------------------------------------------------------------------------------------------------- + +TEST( RimSummaryCalculation, SubstituteVariables_Block ) +{ + // Set up variables with a SUMMARY_BLOCK address at (1,2,3) + SummaryCalculationVariable var; + var.name = "a"; + var.summaryCase = nullptr; + var.summaryAddress = RifEclipseSummaryAddress::blockAddress( "BPR", 1, 2, 3 ); + + std::vector vars = { var }; + + // Substitute to (10,20,30) + auto newAddress = RifEclipseSummaryAddress::blockAddress( "BPR", 10, 20, 30 ); + RimSummaryCalculation::substituteVariables( vars, newAddress ); + + EXPECT_EQ( 10, vars[0].summaryAddress.cellI() ); + EXPECT_EQ( 20, vars[0].summaryAddress.cellJ() ); + EXPECT_EQ( 30, vars[0].summaryAddress.cellK() ); +} + +TEST( RimSummaryCalculation, SubstituteVariables_BlockLgr ) +{ + // Set up variables with a SUMMARY_BLOCK_LGR address + SummaryCalculationVariable var; + var.name = "a"; + var.summaryCase = nullptr; + var.summaryAddress = RifEclipseSummaryAddress::blockLgrAddress( "LBPR", "LGR1", 1, 2, 3 ); + + std::vector vars = { var }; + + // Substitute to new block coordinates - validates the SUMMARY_BLOCK_LGR bug fix + auto newAddress = RifEclipseSummaryAddress::blockLgrAddress( "LBPR", "LGR1", 10, 20, 30 ); + RimSummaryCalculation::substituteVariables( vars, newAddress ); + + EXPECT_EQ( 10, vars[0].summaryAddress.cellI() ); + EXPECT_EQ( 20, vars[0].summaryAddress.cellJ() ); + EXPECT_EQ( 30, vars[0].summaryAddress.cellK() ); +}