#14772 Python: Add GeoMechCase.create_well_bore_stability_plot as object method and forward createWellBoreStabilityPlot to it

RimGeoMechCase_createWellBoreStabilityPlot takes a WellPath object and
a time step and returns the plot. WBS parameters cannot be passed as a
method argument (child objects are not supported as gRPC method
parameters), so the C++ setter is available for the Ricf forwarder and
the Python wrapper copies a WbsParameters object into the created
plot's Parameters child instead.

The Python wrapper moves from Case to GeoMechCase, accepts both a
WellPath object and (deprecated) a well path name, and keeps the
optional parameters argument.
This commit is contained in:
Kristian Bendiksen committed 2026-09-22 14:19:23 +02:00
1 parent 6a2e7a2a21
commit 54c5ef7dad
6 files changed
+223 -58

No files matched your search

@@ -17,21 +17,17 @@
/////////////////////////////////////////////////////////////////////////////////
#include "RicfCreateWellBoreStabilityPlotFeature.h"
#include "RiaLogging.h"
#include "WellLogCommands/RicNewWellBoreStabilityPlotFeature.h"
#include "RicfCommandForwarding.h"
#include "RimGeoMechCase.h"
#include "RimGeoMechView.h"
#include "RimProject.h"
#include "RimWbsParameters.h"
#include "RimWellBoreStabilityPlot.h"
#include "RimWellPath.h"
#include "RimcGeoMechCase.h"
#include "cafPdmFieldScriptingCapability.h"
#include <QAction>
CAF_PDM_SOURCE_INIT( RicfCreateWbsPlotResult, "createWbsPlotResult" );
//--------------------------------------------------------------------------------------------------
@@ -62,48 +58,32 @@ RicfCreateWellBoreStabilityPlotFeature::RicfCreateWellBoreStabilityPlotFeature()
//--------------------------------------------------------------------------------------------------
caf::PdmScriptResponse RicfCreateWellBoreStabilityPlotFeature::execute()
{
RimProject* project = RimProject::current();
const QString commandName = classKeyword();
std::vector<RimGeoMechCase*> geoMechCases = project->descendantsIncludingThisOfType<RimGeoMechCase>();
auto rimCase = RicfForwarding::findCase( m_caseId() );
if ( !rimCase ) return RicfForwarding::errorResponse( rimCase.error(), commandName );
RimGeoMechCase* chosenCase = nullptr;
for ( RimGeoMechCase* geoMechCase : geoMechCases )
auto* geoMechCase = dynamic_cast<RimGeoMechCase*>( rimCase.value() );
if ( !geoMechCase )
{
if ( geoMechCase->caseId() == m_caseId() )
{
chosenCase = geoMechCase;
break;
}
return RicfForwarding::errorResponse( QString( "Could not find GeoMech case with id %1" ).arg( m_caseId() ), commandName );
}
RimWellPath* chosenWellPath = nullptr;
for ( RimWellPath* wellPath : project->allWellPaths() )
{
if ( wellPath->name() == m_wellPath() )
{
chosenWellPath = wellPath;
break;
}
}
RimWellPath* wellPath = RimProject::current()->wellPathByName( m_wellPath() );
if ( !wellPath ) return RicfForwarding::errorResponse( QString( "Could not find well path '%1'" ).arg( m_wellPath() ), commandName );
if ( chosenCase && chosenWellPath && m_timeStep() >= 0 )
{
if ( !chosenWellPath->wellPathGeometry() )
{
QString error =
QString( "The well path %1 has no geometry. Cannot create a Well Bore Stability Plot" ).arg( chosenWellPath->name() );
RiaLogging::error( error.toStdString() );
return caf::PdmScriptResponse( caf::PdmScriptResponse::COMMAND_ERROR, error );
}
RimGeoMechCase_createWellBoreStabilityPlot method( geoMechCase );
method.setWellPath( wellPath );
method.setTimeStep( m_timeStep() );
method.setParameters( m_wbsParameters() );
RimWellBoreStabilityPlot* wbsPlot =
RicNewWellBoreStabilityPlotFeature::createPlot( chosenCase, chosenWellPath, m_timeStep(), m_wbsParameters() );
caf::PdmScriptResponse response;
response.setResult( new RicfCreateWbsPlotResult( wbsPlot->id() ) );
return response;
}
auto result = method.execute();
if ( !result ) return RicfForwarding::errorResponse( result.error(), commandName );
QString error = QString( "createWellBoreStabilityPlot: Could not find GeoMech case with id %1" ).arg( m_caseId() );
RiaLogging::error( error.toStdString() );
return caf::PdmScriptResponse( caf::PdmScriptResponse::COMMAND_ERROR, error );
auto* plot = dynamic_cast<RimWellBoreStabilityPlot*>( result.value() );
if ( !plot ) return RicfForwarding::errorResponse( "Created object is not a Well Bore Stability plot", commandName );
caf::PdmScriptResponse response;
response.setResult( new RicfCreateWbsPlotResult( plot->id() ) );
return response;
}
@@ -47,6 +47,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimcNestedCollectionBase.cpp
${CMAKE_CURRENT_LIST_DIR}/RimcCase.cpp
${CMAKE_CURRENT_LIST_DIR}/RimcViewWindow.cpp
${CMAKE_CURRENT_LIST_DIR}/RimcGeoMechCase.cpp
)
list(APPEND CODE_SOURCE_FILES ${SOURCE_GROUP_SOURCE_FILES})
@@ -0,0 +1,99 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimcGeoMechCase.h"
#include "WellLogCommands/RicNewWellBoreStabilityPlotFeature.h"
#include "RimGeoMechCase.h"
#include "RimWbsParameters.h"
#include "RimWellBoreStabilityPlot.h"
#include "RimWellPath.h"
#include "cafPdmFieldScriptingCapability.h"
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimGeoMechCase, RimGeoMechCase_createWellBoreStabilityPlot, "createWellBoreStabilityPlot" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimGeoMechCase_createWellBoreStabilityPlot::RimGeoMechCase_createWellBoreStabilityPlot( caf::PdmObjectHandle* self )
: caf::PdmObjectCreationMethod( self )
{
CAF_PDM_InitObject( "Create Well Bore Stability Plot", "", "", "Create a Well Bore Stability plot for a well path in this case" );
CAF_PDM_InitScriptableFieldNoDefault( &m_wellPath, "WellPath", "Well Path", "", "", "Well path to create the plot for" );
CAF_PDM_InitScriptableField( &m_timeStep, "TimeStep", 0, "Time Step", "", "", "Zero-based time step index" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimGeoMechCase_createWellBoreStabilityPlot::setWellPath( RimWellPath* wellPath )
{
m_wellPath = wellPath;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimGeoMechCase_createWellBoreStabilityPlot::setTimeStep( int timeStep )
{
m_timeStep = timeStep;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimGeoMechCase_createWellBoreStabilityPlot::setParameters( const RimWbsParameters* parameters )
{
m_parameters = parameters;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<caf::PdmObjectHandle*, QString> RimGeoMechCase_createWellBoreStabilityPlot::execute()
{
auto* geoMechCase = self<RimGeoMechCase>();
if ( !geoMechCase ) return std::unexpected( "No GeoMech case is available." );
RimWellPath* wellPath = m_wellPath();
if ( !wellPath ) return std::unexpected( "No well path specified." );
if ( !wellPath->wellPathGeometry() )
{
return std::unexpected(
QString( "The well path %1 has no geometry. Cannot create a Well Bore Stability Plot" ).arg( wellPath->name() ) );
}
if ( m_timeStep() < 0 ) return std::unexpected( "Time step must be non-negative." );
RimWellBoreStabilityPlot* plot = RicNewWellBoreStabilityPlotFeature::createPlot( geoMechCase, wellPath, m_timeStep(), m_parameters );
if ( !plot ) return std::unexpected( "Could not create Well Bore Stability plot." );
return plot;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimGeoMechCase_createWellBoreStabilityPlot::classKeywordReturnedType() const
{
return RimWellBoreStabilityPlot::classKeywordStatic();
}
@@ -0,0 +1,56 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "cafPdmField.h"
#include "cafPdmObjectHandle.h"
#include "cafPdmObjectMethod.h"
#include "cafPdmPtrField.h"
#include <QString>
class RimWellPath;
class RimWbsParameters;
//==================================================================================================
/// Create a Well Bore Stability plot for a well path in this GeoMech case.
///
/// The plot is created with default parameters. Adjust them through the returned plot's Parameters
/// child object, or pass a RimWbsParameters object from C++ via setParameters().
//==================================================================================================
class RimGeoMechCase_createWellBoreStabilityPlot : public caf::PdmObjectCreationMethod
{
CAF_PDM_HEADER_INIT;
public:
RimGeoMechCase_createWellBoreStabilityPlot( caf::PdmObjectHandle* self );
void setWellPath( RimWellPath* wellPath );
void setTimeStep( int timeStep );
void setParameters( const RimWbsParameters* parameters );
std::expected<caf::PdmObjectHandle*, QString> execute() override;
QString classKeywordReturnedType() const override;
private:
caf::PdmPtrField<RimWellPath*> m_wellPath;
caf::PdmField<int> m_timeStep;
const RimWbsParameters* m_parameters = nullptr;
};
+35 -16
View File
@@ -59,6 +59,7 @@ from .resinsight_classes import (
PropertyType as PropertyType,
Reservoir as Reservoir,
WellBoreStabilityPlot as WellBoreStabilityPlot,
WellPath as WellPath,
WbsParameters as WbsParameters,
)
@@ -892,37 +893,55 @@ def set_grid_property(
raise IndexError
@add_method(Case)
@add_method(GeoMechCase)
def create_well_bore_stability_plot(
self,
well_path: str,
well_path: Union[str, WellPath],
time_step: int,
parameters: Optional[WbsParameters] = None,
) -> Optional[WellBoreStabilityPlot]:
) -> WellBoreStabilityPlot:
"""Create a new well bore stability plot
Arguments:
well_path(str): well path name
well_path(WellPath or str): well path object or well path name
time_step(int): time step
parameters(WbsParameters): optional parameters copied into the created plot
Returns:
:class:`rips.generated.generated_classes.WellBoreStabilityPlot`
"""
pb2_parameters = None
if isinstance(well_path, str):
warnings.warn(
"GeoMechCase.create_well_bore_stability_plot(well_path=<name>) is deprecated, pass a WellPath object instead",
DeprecationWarning,
stacklevel=3,
)
project = self.ancestor(rips.project.Project)
well_path_object = project.well_path_by_name(well_path)
if well_path_object is None:
raise RipsError(f"Could not find well path '{well_path}'")
well_path = well_path_object
plot = self._call_pdm_method_return_value(
"createWellBoreStabilityPlot",
WellBoreStabilityPlot,
well_path=well_path,
time_step=time_step,
)
if parameters is not None:
assert isinstance(parameters, WbsParameters)
pb2_parameters = parameters.pb2_object()
plot_parameters = plot.parameters()
if plot_parameters is not None:
# Copy the public field values only; private members hold the gRPC connection
for attribute in dir(parameters):
if attribute.startswith("_"):
continue
value = getattr(parameters, attribute)
if not callable(value):
setattr(plot_parameters, attribute, value)
plot_parameters.update()
plot_result = self._execute_command(
createWellBoreStabilityPlot=Cmd.CreateWbsPlotRequest(
caseId=self.id,
wellPath=well_path,
timeStep=time_step,
wbsParameters=pb2_parameters,
)
)
project = self.ancestor(rips.project.Project)
plot = project.plot(view_id=plot_result.createWbsPlotResult.viewId)
return plot
@@ -459,3 +459,13 @@ def test_create_saturation_pressure_plots_requires_data(rips_instance, initializ
case = rips_instance.project.load_case(case_path)
with pytest.raises(rips.RipsError):
case.create_saturation_pressure_plots()
def test_create_well_bore_stability_plot_is_geomech_only(
rips_instance, initialize_test
):
# No GeoMech test data is available; verify the method is exposed on the right class
case_path = dataroot.PATH + "/TEST10K_FLT_LGR_NNC/TEST10K_FLT_LGR_NNC.EGRID"
case = rips_instance.project.load_case(case_path)
assert not hasattr(case, "create_well_bore_stability_plot")
assert hasattr(rips.GeoMechCase, "create_well_bore_stability_plot")