From 5a7e0438378c2d327cf269a2e970a70db8a58407 Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Wed, 15 Jul 2026 15:40:38 +0200 Subject: [PATCH] #14345 Add Data Filters to grid ensembles Add a data filter collection to RimReservoirGridEnsemble, so filters can be defined once for the ensemble instead of per realization. The filters are bound to the main case of the ensemble for configuration, and evaluated per case as elsewhere. The filter creation commands are available from a Data Filters submenu on the ensemble node, and the collection node is hidden while empty, matching the case-level behavior. The grid calculation data filter options include the filters of the ensemble the destination case belongs to. --- .../ProjectDataModel/RimGridCalculation.cpp | 28 ++++++++++-- .../UnitTests/RimCellFilterTools-Test.cpp | 44 +++++++++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp b/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp index c38b374c65..77370828f8 100644 --- a/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp @@ -55,6 +55,7 @@ #include "RimOilField.h" #include "RimProject.h" #include "RimReloadCaseTools.h" +#include "RimReservoirGridEnsemble.h" #include "RimResultSelectionUi.h" #include "RimTools.h" @@ -461,12 +462,31 @@ QList RimGridCalculation::calculateValueOptions( const c { options.push_back( caf::PdmOptionItemInfo( "None", nullptr ) ); - if ( m_destinationCase() && m_destinationCase()->dataFilterCollection() ) + if ( m_destinationCase() ) { - for ( RimCellFilter* filter : m_destinationCase()->dataFilterCollection()->filters() ) + if ( auto dataFilterCollection = m_destinationCase()->dataFilterCollection() ) { - if ( !filter ) continue; - options.push_back( caf::PdmOptionItemInfo( filter->fullName(), filter, false, filter->uiIconProvider() ) ); + for ( RimCellFilter* filter : dataFilterCollection->filters() ) + { + if ( !filter ) continue; + options.push_back( caf::PdmOptionItemInfo( filter->fullName(), filter, false, filter->uiIconProvider() ) ); + } + } + + // Also offer the data filters of the grid ensemble the destination case belongs to, if any + if ( auto gridEnsemble = m_destinationCase()->firstAncestorOfType() ) + { + if ( auto dataFilterCollection = gridEnsemble->dataFilterCollection() ) + { + for ( RimCellFilter* filter : dataFilterCollection->filters() ) + { + if ( !filter ) continue; + options.push_back( caf::PdmOptionItemInfo( QString( "%1 : %2" ).arg( gridEnsemble->name(), filter->fullName() ), + filter, + false, + filter->uiIconProvider() ) ); + } + } } } } diff --git a/ApplicationLibCode/UnitTests/RimCellFilterTools-Test.cpp b/ApplicationLibCode/UnitTests/RimCellFilterTools-Test.cpp index 597f78247e..25578f08c7 100644 --- a/ApplicationLibCode/UnitTests/RimCellFilterTools-Test.cpp +++ b/ApplicationLibCode/UnitTests/RimCellFilterTools-Test.cpp @@ -30,9 +30,11 @@ #include "RimCellFilterTools.h" #include "RimCellRangeFilter.h" +#include "RimDataFilterCollection.h" #include "RimEclipsePropertyFilter.h" #include "RimEclipseResultCase.h" #include "RimEclipseResultDefinition.h" +#include "RimReservoirGridEnsemble.h" #include "cafPdmField.h" @@ -104,6 +106,48 @@ TEST( RimCellFilterToolsTest, RangeFilterVisibility ) EXPECT_EQ( visibility->size() - visibleCount, excludeVisibleCount ); } +//-------------------------------------------------------------------------------------------------- +/// Data filters in a grid ensemble collection are bound to the main case of the ensemble, and can +/// be evaluated per case without any view +//-------------------------------------------------------------------------------------------------- +TEST( RimCellFilterToolsTest, GridEnsembleDataFilterCollection ) +{ + auto eclipseCase = openBruggeCase( "Real0", "BRUGGE_0000.EGRID" ); + ASSERT_TRUE( eclipseCase != nullptr ); + + auto ensemble = std::make_unique(); + + // The ensemble takes ownership of the case + RimEclipseResultCase* mainCase = eclipseCase.release(); + ensemble->addCase( mainCase ); + + ASSERT_TRUE( ensemble->dataFilterCollection() != nullptr ); + + // New filters in the ensemble collection are bound to the main case of the ensemble + auto* propertyFilter = ensemble->dataFilterCollection()->addNewPropertyFilter(); + ASSERT_TRUE( propertyFilter != nullptr ); + EXPECT_EQ( mainCase, propertyFilter->resultDefinition()->eclipseCase() ); + + auto* rangeFilter = ensemble->dataFilterCollection()->addNewRangeFilter(); + ASSERT_TRUE( rangeFilter != nullptr ); + rangeFilter->startIndexI = 1; + rangeFilter->startIndexJ = 1; + rangeFilter->startIndexK = 1; + rangeFilter->cellCountI = 10; + rangeFilter->cellCountJ = 10; + rangeFilter->cellCountK = 1; + + auto visibility = RimCellFilterTools::computeReservoirCellVisibility( rangeFilter, mainCase, 0 ); + ASSERT_TRUE( visibility.notNull() ); + + size_t visibleCount = 0; + for ( size_t i = 0; i < visibility->size(); i++ ) + { + if ( visibility->val( i ) ) visibleCount++; + } + EXPECT_EQ( size_t( 10 * 10 * 1 ), visibleCount ); +} + //-------------------------------------------------------------------------------------------------- /// //--------------------------------------------------------------------------------------------------