#13398 Python: Add API for custom well path segments

Add Python GRPC interface support for creating and managing custom segment
intervals for multi-segment wells. This provides API parity with the existing
GUI functionality.

Changes:
- Add createInterval() method to RimCustomSegmentIntervalCollection
- Add GRPC method addCustomSegmentInterval to RimcWellPathCompletionSettings
- Add validation to prevent creating intervals with start_md >= end_md
- Add Python test test_custom_segment_intervals.py with validation test
- Add usage example to modeled_well_path.py

Follows the pattern established by diameter roughness intervals.

Fixes #13398.
This commit is contained in:
Kristian Bendiksen authored and Magne Sjaastad committed 2026-01-13 10:44:55 +01:00
1 parent e34d36ee99
commit a71fb9ad19
6 files changed
+194

No files matched your search

@@ -38,6 +38,19 @@ RimCustomSegmentIntervalCollection::~RimCustomSegmentIntervalCollection()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCustomSegmentInterval* RimCustomSegmentIntervalCollection::createInterval( double startMD, double endMD )
{
auto* interval = new RimCustomSegmentInterval();
interval->setStartMD( startMD );
interval->setEndMD( endMD );
addInterval( interval );
return interval;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -33,6 +33,9 @@ public:
RimCustomSegmentIntervalCollection();
~RimCustomSegmentIntervalCollection() override;
// Domain-specific interval creation
RimCustomSegmentInterval* createInterval( double startMD, double endMD );
// Validation
std::map<QString, QString> validate( const QString& configName = "" ) const override;
@@ -18,6 +18,8 @@
#include "RimcWellPathCompletionSettings.h"
#include "RimCustomSegmentInterval.h"
#include "RimCustomSegmentIntervalCollection.h"
#include "RimDiameterRoughnessInterval.h"
#include "RimDiameterRoughnessIntervalCollection.h"
#include "RimMswCompletionParameters.h"
@@ -29,6 +31,10 @@ CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellPathCompletionSettings,
RimcWellPathCompletionSettings_addDiameterRoughnessInterval,
"AddDiameterRoughnessInterval" );
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellPathCompletionSettings,
RimcWellPathCompletionSettings_addCustomSegmentInterval,
"AddCustomSegmentInterval" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -88,3 +94,65 @@ QString RimcWellPathCompletionSettings_addDiameterRoughnessInterval::classKeywor
{
return RimDiameterRoughnessInterval::classKeywordStatic();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimcWellPathCompletionSettings_addCustomSegmentInterval::RimcWellPathCompletionSettings_addCustomSegmentInterval( caf::PdmObjectHandle* self )
: caf::PdmObjectCreationMethod( self )
{
CAF_PDM_InitObject( "Add Custom Segment Interval" );
CAF_PDM_InitScriptableField( &m_startMD, "StartMd", 0.0, "Start Measured Depth" );
CAF_PDM_InitScriptableField( &m_endMD, "EndMd", 100.0, "End Measured Depth" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<caf::PdmObjectHandle*, QString> RimcWellPathCompletionSettings_addCustomSegmentInterval::execute()
{
auto completionSettings = self<RimWellPathCompletionSettings>();
if ( !completionSettings )
{
return std::unexpected( QString( "Completion settings is null. Cannot add custom segment interval." ) );
}
auto mswParams = completionSettings->mswCompletionParameters();
if ( !mswParams )
{
return std::unexpected( QString( "MSW completion parameters is null. Cannot add custom segment interval." ) );
}
auto intervalCollection = mswParams->customSegmentIntervals();
if ( !intervalCollection )
{
return std::unexpected( QString( "Custom segment interval collection is null. Cannot add interval." ) );
}
// Validate that end MD is greater than start MD
if ( m_endMD() <= m_startMD() )
{
return std::unexpected(
QString( "End MD must be greater than Start MD. Start MD: %1, End MD: %2" ).arg( m_startMD() ).arg( m_endMD() ) );
}
// Create new interval
auto* newInterval = intervalCollection->createInterval( m_startMD(), m_endMD() );
if ( !newInterval )
{
return std::unexpected( QString( "Failed to create custom segment interval." ) );
}
completionSettings->updateAllRequiredEditors();
return newInterval;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimcWellPathCompletionSettings_addCustomSegmentInterval::classKeywordReturnedType() const
{
return RimCustomSegmentInterval::classKeywordStatic();
}
@@ -42,4 +42,22 @@ private:
caf::PdmField<double> m_endMD;
caf::PdmField<double> m_diameter;
caf::PdmField<double> m_roughnessFactor;
};
//==================================================================================================
///
//==================================================================================================
class RimcWellPathCompletionSettings_addCustomSegmentInterval : public caf::PdmObjectCreationMethod
{
CAF_PDM_HEADER_INIT;
public:
RimcWellPathCompletionSettings_addCustomSegmentInterval( caf::PdmObjectHandle* self );
std::expected<caf::PdmObjectHandle*, QString> execute() override;
QString classKeywordReturnedType() const override;
private:
caf::PdmField<double> m_startMD;
caf::PdmField<double> m_endMD;
};
@@ -79,6 +79,14 @@ print(
f"Added diameter roughness intervals: {interval1.start_md}-{interval1.end_md}m and {interval2.start_md}-{interval2.end_md}m"
)
# Add custom segment intervals to define explicit segment boundaries for MSW export
segment1 = completions_settings.add_custom_segment_interval(start_md=3200, end_md=3250)
segment2 = completions_settings.add_custom_segment_interval(start_md=3250, end_md=3320)
segment3 = completions_settings.add_custom_segment_interval(start_md=3320, end_md=3400)
print(
f"Added custom segment intervals: {segment1.start_md}-{segment1.end_md}m, {segment2.start_md}-{segment2.end_md}m, {segment3.start_md}-{segment3.end_md}m"
)
# Optionally update the MSW settings
msw_settings = well_path.msw_settings()
msw_settings.custom_values_for_lateral = False
@@ -0,0 +1,84 @@
import pytest
import rips
def test_custom_segment_intervals(rips_instance, initialize_test):
"""Test the custom segment intervals Python GRPC interface"""
# Create a test well
well_path_coll = rips_instance.project.well_path_collection()
well_path = well_path_coll.add_new_object(rips.ModeledWellPath)
well_path.name = "Test Well for Custom Segments"
completions_settings = well_path.completion_settings()
assert completions_settings is not None, "Completion settings should not be None"
# Test creating first interval
interval1 = completions_settings.add_custom_segment_interval(
start_md=100, end_md=200
)
# Assertions to verify the first interval
assert interval1 is not None, "First interval should not be None"
assert interval1.start_md == 100
assert interval1.end_md == 200
# Test creating a second interval with different values
interval2 = completions_settings.add_custom_segment_interval(
start_md=250, end_md=350
)
# Assertions to verify the second interval
assert interval2 is not None, "Second interval should not be None"
assert interval2.start_md == 250
assert interval2.end_md == 350
# Test default values
interval3 = completions_settings.add_custom_segment_interval()
assert interval3 is not None, "Third interval with defaults should not be None"
assert interval3.start_md == 0.0
assert interval3.end_md == 100.0
def test_custom_segment_interval_properties(rips_instance, initialize_test):
"""Test modifying custom segment interval properties"""
# Create a test well
well_path_coll = rips_instance.project.well_path_collection()
well_path = well_path_coll.add_new_object(rips.ModeledWellPath)
well_path.name = "Test Well for Property Modification"
completions_settings = well_path.completion_settings()
# Create an interval
interval1 = completions_settings.add_custom_segment_interval(
start_md=100, end_md=200
)
# Test modifying interval properties
interval1.start_md = 150
assert interval1.start_md == 150
interval1.end_md = 250
assert interval1.end_md == 250
def test_custom_segment_interval_invalid_range(rips_instance, initialize_test):
"""Test creating interval with invalid range (start_md > end_md) fails"""
# Create a test well
well_path_coll = rips_instance.project.well_path_collection()
well_path = well_path_coll.add_new_object(rips.ModeledWellPath)
well_path.name = "Test Well for Invalid Range"
completions_settings = well_path.completion_settings()
# Attempt to create an interval with start_md > end_md. Should fail.
with pytest.raises(rips.RipsError, match="End MD must be greater than Start MD"):
completions_settings.add_custom_segment_interval(start_md=200, end_md=100)
# Also test equal values (start_md == end_md), which should also fail
with pytest.raises(rips.RipsError, match="End MD must be greater than Start MD"):
completions_settings.add_custom_segment_interval(start_md=150, end_md=150)