#14772 Python: Add Reservoir.export_msw_completions as object method and forward exportMsw to it

RimEclipseCase_exportMswCompletions takes WellPath objects and an
explicit export folder. RicfExportMsw resolves the well path name and
export folder and forwards to it. Case.export_msw() in Python is kept
and emits a DeprecationWarning.
This commit is contained in:
Kristian Bendiksen committed 2026-09-22 14:19:23 +02:00
1 parent afe8f39f6b
commit b40d19f55f
5 files changed
+196 -32

No files matched your search

@@ -18,23 +18,15 @@
#include "RicfExportMsw.h"
#include "RicfApplicationTools.h"
#include "RicfCommandFileExecutor.h"
#include "RicfCommandForwarding.h"
#include "RiaApplication.h"
#include "RiaLogging.h"
#include "RimEclipseCase.h"
#include "RimEclipseCaseCollection.h"
#include "RimFishbones.h"
#include "RimFishbonesCollection.h"
#include "RimOilField.h"
#include "RimProject.h"
#include "RimWellPath.h"
#include "RimWellPathCollection.h"
#include "CompletionExportCommands/RicExportCompletionDataSettingsUi.h"
#include "CompletionExportCommands/RicWellPathExportMswCompletionsImpl.h"
#include "RimcEclipseCase.h"
#include "cafPdmFieldScriptingCapability.h"
@@ -58,39 +50,37 @@ RicfExportMsw::RicfExportMsw()
//--------------------------------------------------------------------------------------------------
caf::PdmScriptResponse RicfExportMsw::execute()
{
using TOOLS = RicfApplicationTools;
const QString commandName = classKeyword();
RicExportCompletionDataSettingsUi exportSettings;
auto rimCase = RicfForwarding::findCase( m_caseId() );
if ( !rimCase ) return RicfForwarding::errorResponse( rimCase.error(), commandName );
auto eclipseCase = TOOLS::caseFromId( m_caseId() );
auto* eclipseCase = dynamic_cast<RimEclipseCase*>( rimCase.value() );
if ( !eclipseCase )
{
QString error = QString( "exportMsw: Could not find case with ID %1." ).arg( m_caseId() );
RiaLogging::error( error.toStdString() );
return caf::PdmScriptResponse( caf::PdmScriptResponse::COMMAND_ERROR, error );
return RicfForwarding::errorResponse( QString( "Case with ID %1 is not an Eclipse case" ).arg( m_caseId() ), commandName );
}
RimWellPath* wellPath = RimProject::current()->wellPathByName( m_wellPathName() );
if ( !wellPath )
{
return RicfForwarding::errorResponse( QString( "Could not find well path with name %1" ).arg( m_wellPathName() ), commandName );
}
// Resolve the export folder from the command file executor state. The Rimc method requires an explicit folder.
QString exportFolder = RicfCommandFileExecutor::instance()->getExportPath( RicfCommandFileExecutor::ExportType::COMPLETIONS );
if ( exportFolder.isNull() )
{
exportFolder = RiaApplication::instance()->createAbsolutePathFromProjectRelativePath( "completions" );
}
exportSettings.caseToApply = eclipseCase;
exportSettings.folder = exportFolder;
exportSettings.includePerforations = m_includePerforations;
exportSettings.includeFishbones = m_includeFishbones;
exportSettings.includeFractures = m_includeFractures;
exportSettings.fileSplit = m_fileSplit;
RimWellPath* wellPath = RimProject::current()->wellPathByName( m_wellPathName );
if ( !wellPath )
{
QString error = QString( "exportMsw: Could not find well path with name %1" ).arg( m_wellPathName() );
RiaLogging::error( error.toStdString() );
return caf::PdmScriptResponse( caf::PdmScriptResponse::COMMAND_ERROR, error );
}
RimEclipseCase_exportMswCompletions method( eclipseCase );
method.setWellPaths( { wellPath } );
method.setExportFolder( exportFolder );
method.setFileSplit( m_fileSplit() );
method.setIncludePerforations( m_includePerforations() );
method.setIncludeFishbones( m_includeFishbones() );
method.setIncludeFractures( m_includeFractures() );
RicWellPathExportMswCompletionsImpl::exportWellSegmentsForAllCompletions( exportSettings, { wellPath } );
return caf::PdmScriptResponse();
return RicfForwarding::toScriptResponse( method.execute(), commandName );
}
@@ -23,6 +23,7 @@
#include "RiaKeyValueStoreUtil.h"
#include "CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.h"
#include "CompletionExportCommands/RicWellPathExportMswCompletionsImpl.h"
#include "ExportCommands/RicEclipseCellResultToFileImpl.h"
#include "RifInputPropertyLoader.h"
@@ -828,3 +829,110 @@ std::expected<caf::PdmObjectHandle*, QString> RimEclipseCase_exportCompletions::
return nullptr;
}
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimEclipseCase, RimEclipseCase_exportMswCompletions, "exportMswCompletions" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEclipseCase_exportMswCompletions::RimEclipseCase_exportMswCompletions( caf::PdmObjectHandle* self )
: caf::PdmVoidObjectMethod( self )
{
CAF_PDM_InitObject( "Export MSW Completions", "", "", "Export the Multi Segment Well model keywords for well paths in this case" );
CAF_PDM_InitScriptableFieldNoDefault( &m_wellPaths, "WellPaths", "Well Paths", "", "", "Well paths to export" );
CAF_PDM_InitScriptableField( &m_exportFolder, "ExportFolder", QString(), "Export Folder", "", "", "Folder to write the export files to" );
CAF_PDM_InitScriptableField( &m_fileSplit,
"FileSplit",
RicExportCompletionDataSettingsUi::ExportSplitType(),
"File Split",
"",
"",
"Controls how export data is split into files" );
CAF_PDM_InitScriptableField( &m_includePerforations, "IncludePerforations", true, "Include Perforations" );
CAF_PDM_InitScriptableField( &m_includeFishbones, "IncludeFishbones", true, "Include Fishbones" );
CAF_PDM_InitScriptableField( &m_includeFractures, "IncludeFractures", true, "Include Fractures" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseCase_exportMswCompletions::setWellPaths( const std::vector<RimWellPath*>& wellPaths )
{
m_wellPaths.setValue( wellPaths );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseCase_exportMswCompletions::setExportFolder( const QString& exportFolder )
{
m_exportFolder = exportFolder;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseCase_exportMswCompletions::setFileSplit( RicExportCompletionDataSettingsUi::ExportSplit fileSplit )
{
m_fileSplit = fileSplit;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseCase_exportMswCompletions::setIncludePerforations( bool enable )
{
m_includePerforations = enable;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseCase_exportMswCompletions::setIncludeFishbones( bool enable )
{
m_includeFishbones = enable;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseCase_exportMswCompletions::setIncludeFractures( bool enable )
{
m_includeFractures = enable;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<caf::PdmObjectHandle*, QString> RimEclipseCase_exportMswCompletions::execute()
{
auto* eclipseCase = self<RimEclipseCase>();
if ( !eclipseCase ) return std::unexpected( "No case is available." );
if ( m_exportFolder().isEmpty() ) return std::unexpected( "No export folder specified." );
QDir folder( m_exportFolder() );
if ( !folder.exists() ) return std::unexpected( QString( "The export folder '%1' does not exist." ).arg( m_exportFolder() ) );
std::vector<RimWellPath*> wellPaths = m_wellPaths.ptrReferencedObjectsByType();
if ( wellPaths.empty() ) return std::unexpected( "No well paths specified." );
eclipseCase->ensureReservoirCaseIsOpen();
if ( !eclipseCase->eclipseCaseData() )
{
return std::unexpected( QString( "No data available for case '%1'" ).arg( eclipseCase->caseUserDescription() ) );
}
RicExportCompletionDataSettingsUi exportSettings;
exportSettings.caseToApply = eclipseCase;
exportSettings.folder = m_exportFolder();
exportSettings.fileSplit = m_fileSplit();
exportSettings.includePerforations = m_includePerforations();
exportSettings.includeFishbones = m_includeFishbones();
exportSettings.includeFractures = m_includeFractures();
RicWellPathExportMswCompletionsImpl::exportWellSegmentsForAllCompletions( exportSettings, wellPaths );
return nullptr;
}
@@ -247,3 +247,31 @@ private:
caf::PdmField<bool> m_exportComments;
caf::PdmField<bool> m_exportWelspec;
};
//==================================================================================================
/// Export the Multi Segment Well (MSW) model keywords (WELSEGS, COMPSEGS, ...) for well paths in this case.
//==================================================================================================
class RimEclipseCase_exportMswCompletions : public caf::PdmVoidObjectMethod
{
CAF_PDM_HEADER_INIT;
public:
RimEclipseCase_exportMswCompletions( caf::PdmObjectHandle* self );
void setWellPaths( const std::vector<RimWellPath*>& wellPaths );
void setExportFolder( const QString& exportFolder );
void setFileSplit( RicExportCompletionDataSettingsUi::ExportSplit fileSplit );
void setIncludePerforations( bool enable );
void setIncludeFishbones( bool enable );
void setIncludeFractures( bool enable );
std::expected<caf::PdmObjectHandle*, QString> execute() override;
private:
caf::PdmPtrArrayField<RimWellPath*> m_wellPaths;
caf::PdmField<QString> m_exportFolder;
caf::PdmField<RicExportCompletionDataSettingsUi::ExportSplitType> m_fileSplit;
caf::PdmField<bool> m_includePerforations;
caf::PdmField<bool> m_includeFishbones;
caf::PdmField<bool> m_includeFractures;
};
+8
View File
@@ -490,9 +490,17 @@ def export_msw(self, well_path: str) -> Any:
"""
Export Eclipse Multi-segment-well model to file
Deprecated: use Reservoir.export_msw_completions(well_paths=[...], export_folder=...) instead,
which takes WellPath objects and an explicit export folder.
Arguments:
well_path(str): Well path name
"""
warnings.warn(
"Case.export_msw() is deprecated, use Reservoir.export_msw_completions() instead",
DeprecationWarning,
stacklevel=3,
)
return self._execute_command(
exportMsw=Cmd.ExportMswRequest(caseId=self.id, wellPath=well_path)
)
@@ -1,10 +1,12 @@
import sys
import os
import pytest
import tempfile
sys.path.insert(1, os.path.join(sys.path[0], "../../"))
import dataroot
import rips
from rips import PressureDrop, ReferenceMdType, WellTypeForExport
@@ -192,6 +194,34 @@ def test_msw_export(rips_instance, initialize_test):
# additional file system checks
def test_export_msw_completions(rips_instance, initialize_test):
"""Test the object method for MSW export with explicit well path objects and folder."""
project_path = dataroot.PATH + "/msw-export/project-files/perf_lateral.rsp"
project = rips_instance.project.open(path=project_path)
case = project.cases()[0]
well_y1 = project.well_path_by_name("Well-1 Y1")
assert well_y1 is not None
with tempfile.TemporaryDirectory(prefix="rips") as tmpdirname:
case.export_msw_completions(
well_paths=[well_y1],
export_folder=tmpdirname,
file_split=rips.CompletionExportSplit.UNIFIED_FILE,
)
files = os.listdir(tmpdirname)
assert len(files) == 1
with open(os.path.join(tmpdirname, files[0])) as f:
content = f.read()
assert "WELSEGS" in content
assert "COMPSEGS" in content
with pytest.raises(rips.RipsError):
case.export_msw_completions(well_paths=[well_y1])
with pytest.raises(rips.RipsError):
case.export_msw_completions(export_folder=tempfile.gettempdir())
def test_msw_settings_read(rips_instance, initialize_test):
"""Test reading MSW settings from project.