Python: Add scriptable Data Filters API for combined filters

Expose the case-level RimDataFilterCollection (introduced for
case-shared cell filters) to Python so scripts can build combined
filters with property-filter and IJK-range children without going
through a view.

Scriptable surface:
- RimEclipseCase.m_dataFilterCollection -> case.data_filter_collection()
- RimDataFilterCollection (object + Filters child array) -> filters()
- RimEclipsePropertyFilter (object + m_lowerBound + m_upperBound) ->
  pf.lower_bound, pf.upper_bound
- RimCellRangeFilter (object + start_index_{i,j,k}, cell_count_{i,j,k})

New CAF object methods under ProjectDataModelCommands/:
- RimcDataFilterCollection: add_combined_filter(name, combine_mode)
- RimcCombinedFilter: add_property_filter(result_variable, result_type)
  and add_range_filter(name, start_{i,j,k}, cell_count_{i,j,k}).

Bounds are not part of add_property_filter because the Python class
generator emits a bare nan literal as the default for double fields
initialised to quiet_NaN, which fails to import. Bounds are set via
the now-scriptable lower_bound/upper_bound fields after creation,
matching the update() pattern used elsewhere in the API.

RicEclipsePropertyFilterFeatureImpl::addPropertyFilterToCombinedFilter
now returns the new RimEclipsePropertyFilter* so the CAF method can
hand it back to Python. setDefaults is relaxed to be view-tolerant:
case-level filters have no Rim3dView ancestor and the eclipse case is
already wired by RimEclipsePropertyFilter::setCase via the owning
collection's onItemsChanged hook.

End-to-end coverage in tests/test_combined_filter.py: round-trip
create, mode toggle, property-filter add/mutate, range-filter
add/mutate. Example script in PythonExamples builds an AND combined
filter that gates PERMX between 100 and 20000 against a K=5..10
slice.
This commit is contained in:
Kristian Bendiksen committed 2026-05-15 08:37:46 +02:00
1 parent 4ed6d70b73
commit eb70d32eb1
13 files changed
+528 -24

No files matched your search

@@ -75,9 +75,9 @@ void RicEclipsePropertyFilterFeatureImpl::addPropertyFilter( RimEclipsePropertyF
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicEclipsePropertyFilterFeatureImpl::addPropertyFilterToCombinedFilter( RimCombinedFilter* combined )
RimEclipsePropertyFilter* RicEclipsePropertyFilterFeatureImpl::addPropertyFilterToCombinedFilter( RimCombinedFilter* combined )
{
if ( !combined ) return;
if ( !combined ) return nullptr;
auto* propertyFilter = new RimEclipsePropertyFilter();
combined->addFilter( propertyFilter );
@@ -94,6 +94,8 @@ void RicEclipsePropertyFilterFeatureImpl::addPropertyFilterToCombinedFilter( Rim
combined->updateConnectedEditors();
Riu3DMainWindowTools::selectAsCurrentItem( propertyFilter, false );
return propertyFilter;
}
//--------------------------------------------------------------------------------------------------
@@ -139,13 +141,17 @@ void RicEclipsePropertyFilterFeatureImpl::setDefaults( RimEclipsePropertyFilter*
{
CVF_ASSERT( propertyFilter );
auto reservoirView = propertyFilter->firstAncestorOrThisOfTypeAsserted<RimEclipseView>();
propertyFilter->resultDefinition()->setEclipseCase( reservoirView->eclipseCase() );
if ( !RiaResultNames::isPerCellFaceResult( reservoirView->cellResult()->resultVariable() ) )
// View-tolerant: case-level data filters have no RimEclipseView ancestor; the result
// definition's eclipse case is already wired by RimEclipsePropertyFilter::setCase via the
// owning collection's onItemsChanged hook.
if ( auto* reservoirView = propertyFilter->firstAncestorOrThisOfType<RimEclipseView>() )
{
propertyFilter->resultDefinition()->simpleCopy( reservoirView->cellResult() );
propertyFilter->resultDefinition()->setEclipseCase( reservoirView->eclipseCase() );
if ( !RiaResultNames::isPerCellFaceResult( reservoirView->cellResult()->resultVariable() ) )
{
propertyFilter->resultDefinition()->simpleCopy( reservoirView->cellResult() );
}
}
propertyFilter->resultDefinition()->loadResult();
@@ -40,9 +40,9 @@ public:
static std::vector<RimEclipsePropertyFilter*> selectedPropertyFilters();
static std::vector<RimEclipsePropertyFilterCollection*> selectedPropertyFilterCollections();
static void addPropertyFilter( RimEclipsePropertyFilterCollection* propertyFilterCollection );
static void addPropertyFilterToCombinedFilter( RimCombinedFilter* combined );
static void insertPropertyFilter( RimEclipsePropertyFilterCollection* propertyFilterCollection, size_t index );
static void addPropertyFilter( RimEclipsePropertyFilterCollection* propertyFilterCollection );
static RimEclipsePropertyFilter* addPropertyFilterToCombinedFilter( RimCombinedFilter* combined );
static void insertPropertyFilter( RimEclipsePropertyFilterCollection* propertyFilterCollection, size_t index );
static bool isPropertyFilterCommandAvailable( caf::PdmObjectHandle* object );