Python: Add cell filter reference to perforation intervals

Lets a script attach a cell filter to a RimPerforationInterval as a
typed reference so subsequent work can gate completion-export
connections by the filter's cell mask. Pure metadata in this commit:
no consumer of RimPerforationInterval is modified, the field exists
purely as an API surface.

Add a non-scriptable caf::PdmPtrField<RimCellFilter*> m_cellFilter to
RimPerforationInterval with public cellFilter()/setCellFilter()
accessors and a "Cell Filter" group in the property panel between the
existing geometry/flow group and the Date Settings group. The field is
deliberately non-scriptable: a scriptable PdmPtrField round-trips in
Python as an opaque "ClassName:0xADDR" string rather than a resolved
object, defeating the point. Two CAF object methods on
RimcPerforationInterval expose the relationship:

- AddFilter (perf.add_filter(filter)) sets the pointer; replaces any
  existing filter (single filter per perforation).
- cell_filter (perf.cell_filter()) resolves to the referenced
  RimCellFilter object on read, mirroring the valve.template() pattern.

Composition stays in RimCombinedFilter: a script that wants AND/OR
across multiple filters attaches a combined filter as the single
filter on the perforation.

The Python example in PythonExamples builds a case-level AND combined
filter (PERMX 100..20000 AND K=5..10), creates a modeled well path
with two perforation intervals, and shares the same filter between
them. test_perforation_filter.py covers single-set, replace-on-second-
add, and shared-pointer round-trips.
This commit is contained in:
Kristian Bendiksen committed 2026-05-15 08:37:46 +02:00
1 parent 3a1ff7a1e3
commit c9456bba62
6 files changed
+324

No files matched your search

@@ -26,6 +26,7 @@
#include "RigCaseCellResultsData.h"
#include "Well/RigWellPath.h"
#include "RimCellFilter.h"
#include "RimPerforationCollection.h"
#include "RimProject.h"
#include "RimWellLogTrack.h"
@@ -63,6 +64,12 @@ RimPerforationInterval::RimPerforationInterval()
CAF_PDM_InitScriptableFieldNoDefault( &m_valves, "Valves", "Valves" );
// Non-scriptable: a scriptable PdmPtrField surfaces in Python as an opaque address string.
// Keeping the field non-scriptable makes the generator emit a `cell_filter()` accessor that
// resolves to a CellFilter object (same pattern as `valve.template()`). The setter goes
// through RimcPerforationInterval_addFilter ("AddFilter").
CAF_PDM_InitFieldNoDefault( &m_cellFilter, "CellFilter", "Cell Filter" );
nameField()->uiCapability()->setUiReadOnly( true );
m_startMD.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() );
@@ -246,6 +253,22 @@ std::vector<RimWellPathValve*> RimPerforationInterval::valves() const
return allValves;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCellFilter* RimPerforationInterval::cellFilter() const
{
return m_cellFilter();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimPerforationInterval::setCellFilter( RimCellFilter* filter )
{
m_cellFilter = filter;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -382,6 +405,9 @@ void RimPerforationInterval::defineUiOrdering( QString uiConfigName, caf::PdmUiO
uiOrdering.add( &m_skinFactor );
uiOrdering.add( &m_completionNumber );
auto filterGrp = uiOrdering.addNewGroup( "Cell Filter" );
filterGrp->add( &m_cellFilter );
auto dateGrp = uiOrdering.addNewGroup( "Date Settings" );
dateGrp->setCollapsedByDefault();
dateGrp->add( &m_useCustomStartDate );
@@ -394,6 +420,30 @@ void RimPerforationInterval::defineUiOrdering( QString uiConfigName, caf::PdmUiO
uiOrdering.skipRemainingFields();
}
//--------------------------------------------------------------------------------------------------
/// Populate the "Cell Filter" dropdown with every RimCellFilter reachable in the project so the
/// user can pick one. Without this the field renders as an empty selector.
//--------------------------------------------------------------------------------------------------
QList<caf::PdmOptionItemInfo> RimPerforationInterval::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
{
QList<caf::PdmOptionItemInfo> options;
if ( fieldNeedingOptions == &m_cellFilter )
{
options.push_back( caf::PdmOptionItemInfo( "None", static_cast<RimCellFilter*>( nullptr ) ) );
if ( auto* project = RimProject::current() )
{
for ( RimCellFilter* filter : project->descendantsIncludingThisOfType<RimCellFilter>() )
{
if ( filter ) options.push_back( caf::PdmOptionItemInfo( filter->name(), filter ) );
}
}
}
return options;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -28,9 +28,11 @@
#include "cafPdmChildArrayField.h"
#include "cafPdmField.h"
#include "cafPdmObject.h"
#include "cafPdmPtrField.h"
#include <QDate>
class RimCellFilter;
class RimWellPathValve;
//==================================================================================================
@@ -68,6 +70,9 @@ public:
std::vector<RimWellPathValve*> valves() const;
void updateAllReferringTracks();
RimCellFilter* cellFilter() const;
void setCellFilter( RimCellFilter* filter );
// RimWellPathCompletionInterface overrides
bool isEnabled() const override;
RiaDefines::WellPathComponentType componentType() const override;
@@ -84,6 +89,7 @@ protected:
void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override;
void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
void initAfterRead() override;
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override;
private:
caf::PdmField<double> m_startMD;
@@ -100,5 +106,7 @@ private:
caf::PdmChildArrayField<RimWellPathValve*> m_valves;
caf::PdmPtrField<RimCellFilter*> m_cellFilter;
caf::PdmField<bool> m_startOfHistory_OBSOLETE;
};