#14772 Python: Add EclipseView.export_visible_cells as object method and forward exportVisibleCells to it

RimEclipseView_exportVisibleCells writes a FLUXNUM/MULTNUM/ACTNUM
keyword based on cell visibility in the view and requires an explicit
export file. The export keyword reuses
RicSaveEclipseInputVisibleCellsUi::ExportKeyword, registered as the
Python StrEnum VisibleCellsExportKeyword.

RicSaveEclipseInputVisibleCellsFeature::exportVisibleCells() is split
out of executeCommand() to return errors via std::expected instead of
only logging them.

RicfExportVisibleCells resolves the export folder from the command file
executor's CELLS export path and forwards to the object method. The
hand-written View.export_visible_cells() wrapper is removed since the
generated EclipseView method shadows it.
This commit is contained in:
Kristian Bendiksen committed 2026-09-22 14:19:23 +02:00
1 parent 1f06f02b39
commit 001a515c68
9 files changed
+224 -82

No files matched your search

@@ -18,28 +18,17 @@
#include "RicfExportVisibleCells.h"
#include "RiaFilePathTools.h"
#include "RiaViewRedrawScheduler.h"
#include "RiaApplication.h"
#include "ExportCommands/RicSaveEclipseInputVisibleCellsFeature.h"
#include "ExportCommands/RicSaveEclipseInputVisibleCellsUi.h"
#include "RicfApplicationTools.h"
#include "RicfCommandFileExecutor.h"
#include "RicfCommandForwarding.h"
#include "RiaApplication.h"
#include "RiaLogging.h"
#include "RimEclipseCase.h"
#include "RimEclipseCaseCollection.h"
#include "RimEclipseCellColors.h"
#include "RimEclipseView.h"
#include "RimOilField.h"
#include "RimProject.h"
#include "RifEclipseInputFileTools.h"
#include "RimcEclipseView.h"
#include "cafPdmFieldScriptingCapability.h"
#include <cafUtils.h>
#include <QDir>
@@ -77,12 +66,13 @@ RicfExportVisibleCells::RicfExportVisibleCells()
//--------------------------------------------------------------------------------------------------
caf::PdmScriptResponse RicfExportVisibleCells::execute()
{
const QString commandName = classKeyword();
if ( m_caseId < 0 || ( m_viewName().isEmpty() && m_viewId() < 0 ) )
{
QString error( "exportVisibleCells: CaseId or view name or view id not specified" );
RiaLogging::error( error.toStdString() );
return caf::PdmScriptResponse( caf::PdmScriptResponse::COMMAND_ERROR, error );
return RicfForwarding::errorResponse( "CaseId or view name or view id not specified", commandName );
}
RimEclipseView* eclipseView = nullptr;
if ( m_viewId() >= 0 )
{
@@ -94,45 +84,33 @@ caf::PdmScriptResponse RicfExportVisibleCells::execute()
}
if ( !eclipseView )
{
QString error( QString( "exportVisibleCells: Could not find view of id %1 or named '%2' in case ID %3" )
.arg( m_viewId() )
.arg( m_viewName() )
.arg( m_caseId() ) );
RiaLogging::error( error.toStdString() );
return caf::PdmScriptResponse( caf::PdmScriptResponse::COMMAND_ERROR, error );
return RicfForwarding::errorResponse( QString( "Could not find view of id %1 or named '%2' in case ID %3" )
.arg( m_viewId() )
.arg( m_viewName() )
.arg( m_caseId() ),
commandName );
}
// Resolve the default export folder from the command file executor state. The Rimc method requires an explicit file.
QString exportFolder = RicfCommandFileExecutor::instance()->getExportPath( RicfCommandFileExecutor::ExportType::CELLS );
if ( exportFolder.isNull() )
{
exportFolder = RiaApplication::instance()->currentProjectPath();
}
RiaViewRedrawScheduler::instance()->clearViewsScheduledForUpdate();
RicSaveEclipseInputVisibleCellsUi exportSettings;
buildExportSettings( exportFolder, &exportSettings );
RicSaveEclipseInputVisibleCellsFeature::executeCommand( eclipseView, exportSettings, "exportVisibleCells" );
return caf::PdmScriptResponse();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicfExportVisibleCells::buildExportSettings( const QString& exportFolder, RicSaveEclipseInputVisibleCellsUi* exportSettings )
{
QDir baseDir( exportFolder );
exportSettings->exportFilename = baseDir.absoluteFilePath( QString( "%1.grdecl" ).arg( m_exportKeyword().text() ) );
if ( m_exportKeyword == ExportKeyword::FLUXNUM )
exportSettings->exportKeyword = RicSaveEclipseInputVisibleCellsUi::FLUXNUM;
else if ( m_exportKeyword == ExportKeyword::MULTNUM )
exportSettings->exportKeyword = RicSaveEclipseInputVisibleCellsUi::MULTNUM;
RicSaveEclipseInputVisibleCellsUi::ExportKeyword exportKeyword = RicSaveEclipseInputVisibleCellsUi::FLUXNUM;
if ( m_exportKeyword == ExportKeyword::MULTNUM )
exportKeyword = RicSaveEclipseInputVisibleCellsUi::MULTNUM;
else if ( m_exportKeyword == ExportKeyword::ACTNUM )
exportSettings->exportKeyword = RicSaveEclipseInputVisibleCellsUi::ACTNUM;
exportKeyword = RicSaveEclipseInputVisibleCellsUi::ACTNUM;
exportSettings->visibleActiveCellsValue = m_visibleActiveCellsValue;
exportSettings->hiddenActiveCellsValue = m_hiddenActiveCellsValue;
exportSettings->inactiveCellsValue = m_inactiveCellsValue;
RimEclipseView_exportVisibleCells method( eclipseView );
method.setExportFile( baseDir.absoluteFilePath( QString( "%1.grdecl" ).arg( m_exportKeyword().text() ) ) );
method.setExportKeyword( exportKeyword );
method.setVisibleActiveCellsValue( m_visibleActiveCellsValue() );
method.setHiddenActiveCellsValue( m_hiddenActiveCellsValue() );
method.setInactiveCellsValue( m_inactiveCellsValue() );
return RicfForwarding::toScriptResponse( method.execute(), commandName );
}
@@ -25,7 +25,6 @@
#include "cafPdmField.h"
class RimEclipseView;
class RicSaveEclipseInputVisibleCellsUi;
//==================================================================================================
//
@@ -49,8 +48,6 @@ public:
caf::PdmScriptResponse execute() override;
private:
void buildExportSettings( const QString& exportFolder, RicSaveEclipseInputVisibleCellsUi* exportSettings );
caf::PdmField<int> m_caseId;
caf::PdmField<int> m_viewId;
caf::PdmField<QString> m_viewName;
@@ -71,6 +71,23 @@ void RicSaveEclipseInputVisibleCellsFeature::executeCommand( RimEclipseView*
const RicSaveEclipseInputVisibleCellsUi& exportSettings,
const QString& logPrefix )
{
if ( auto result = exportVisibleCells( view, exportSettings ); !result )
{
RiaLogging::error( std::format( "{}: {}", logPrefix, result.error() ) );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<void, QString>
RicSaveEclipseInputVisibleCellsFeature::exportVisibleCells( RimEclipseView* view, const RicSaveEclipseInputVisibleCellsUi& exportSettings )
{
if ( !view || !view->eclipseCase() || !view->eclipseCase()->eclipseCaseData() )
{
return std::unexpected( "The view has no case data." );
}
std::vector<double> values;
cvf::UByteArray visibleCells;
view->calculateCurrentTotalCellVisibility( &visibleCells, view->currentTimeStep() );
@@ -97,8 +114,7 @@ void RicSaveEclipseInputVisibleCellsFeature::executeCommand( RimEclipseView*
QFile exportFile( exportSettings.exportFilename );
if ( !exportFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
{
RiaLogging::error( std::format( "{}: Unable to open file '{}' for writing.", logPrefix, exportSettings.exportFilename() ) );
return;
return std::unexpected( QString( "Unable to open file '%1' for writing." ).arg( exportSettings.exportFilename() ) );
}
int valuesPerRow = 5;
@@ -107,6 +123,7 @@ void RicSaveEclipseInputVisibleCellsFeature::executeCommand( RimEclipseView*
exportSettings.exportKeyword().text(),
values,
valuesPerRow );
return {};
}
//--------------------------------------------------------------------------------------------------
@@ -20,6 +20,8 @@
#include "cafCmdFeature.h"
#include <expected>
class RimEclipseView;
class RicSaveEclipseInputVisibleCellsUi;
@@ -33,6 +35,7 @@ class RicSaveEclipseInputVisibleCellsFeature : public caf::CmdFeature
public:
static void openDialogAndExecuteCommand( RimEclipseView* view );
static void executeCommand( RimEclipseView* view, const RicSaveEclipseInputVisibleCellsUi& exportSettings, const QString& logPrefix );
static std::expected<void, QString> exportVisibleCells( RimEclipseView* view, const RicSaveEclipseInputVisibleCellsUi& exportSettings );
protected:
bool isCommandEnabled() const override;
@@ -21,6 +21,7 @@
#include "RiaApplication.h"
#include "RiaPreferences.h"
#include "cafPdmScriptEnumNameRegistry.h"
#include "cafPdmUiFilePathEditor.h"
#include <QDir>
@@ -37,6 +38,19 @@ void RicSaveEclipseInputVisibleCellsUi::ExportKeywordEnum::setUp()
}
} // namespace caf
namespace
{
// Pin the Python StrEnum class name for the export keyword used by RimEclipseView_exportVisibleCells
struct RegisterScriptEnumNames
{
RegisterScriptEnumNames()
{
caf::PdmScriptEnumNameRegistry::registerName<RicSaveEclipseInputVisibleCellsUi::ExportKeyword>( "VisibleCellsExportKeyword" );
}
};
const RegisterScriptEnumNames s_registerScriptEnumNames;
} // namespace
CAF_PDM_SOURCE_INIT( RicSaveEclipseInputVisibleCellsUi, "RicSaveEclipseInputVisibleCellsUi" );
//--------------------------------------------------------------------------------------------------
@@ -19,6 +19,9 @@
#include "RimcEclipseView.h"
#include "ExportCommands/RicEclipseCellResultToFileImpl.h"
#include "ExportCommands/RicSaveEclipseInputVisibleCellsFeature.h"
#include "RiaViewRedrawScheduler.h"
#include "RigResultAccessor.h"
#include "RigResultAccessorFactory.h"
@@ -181,3 +184,105 @@ QString RimEclipseView_exportCurrentProperty::defaultFileBaseName( const RimEcli
return caf::Utils::makeValidFileBasename( fileName );
}
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimEclipseView, RimEclipseView_exportVisibleCells, "exportVisibleCells" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEclipseView_exportVisibleCells::RimEclipseView_exportVisibleCells( caf::PdmObjectHandle* self )
: caf::PdmVoidObjectMethod( self )
{
CAF_PDM_InitObject( "Export Visible Cells", "", "", "Export a GRDECL keyword with one value per cell based on cell visibility in the view" );
CAF_PDM_InitScriptableField( &m_exportFile, "ExportFile", QString(), "Export File", "", "", "Full path of the file to write" );
CAF_PDM_InitScriptableField( &m_exportKeyword,
"ExportKeyword",
RicSaveEclipseInputVisibleCellsUi::FLUXNUM,
"Export Keyword",
"",
"",
"GRDECL keyword to write" );
CAF_PDM_InitScriptableField( &m_visibleActiveCellsValue,
"VisibleActiveCellsValue",
1,
"Visible Active Cells Value",
"",
"",
"Value for visible active cells" );
CAF_PDM_InitScriptableField( &m_hiddenActiveCellsValue,
"HiddenActiveCellsValue",
0,
"Hidden Active Cells Value",
"",
"",
"Value for hidden active cells" );
CAF_PDM_InitScriptableField( &m_inactiveCellsValue, "InactiveCellsValue", 0, "Inactive Cells Value", "", "", "Value for inactive cells" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseView_exportVisibleCells::setExportFile( const QString& exportFile )
{
m_exportFile = exportFile;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseView_exportVisibleCells::setExportKeyword( RicSaveEclipseInputVisibleCellsUi::ExportKeyword exportKeyword )
{
m_exportKeyword = exportKeyword;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseView_exportVisibleCells::setVisibleActiveCellsValue( int value )
{
m_visibleActiveCellsValue = value;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseView_exportVisibleCells::setHiddenActiveCellsValue( int value )
{
m_hiddenActiveCellsValue = value;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseView_exportVisibleCells::setInactiveCellsValue( int value )
{
m_inactiveCellsValue = value;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<caf::PdmObjectHandle*, QString> RimEclipseView_exportVisibleCells::execute()
{
auto* view = self<RimEclipseView>();
if ( !view ) return std::unexpected( "No view is available." );
if ( m_exportFile().isEmpty() ) return std::unexpected( "No export file specified." );
RiaViewRedrawScheduler::instance()->clearViewsScheduledForUpdate();
RicSaveEclipseInputVisibleCellsUi exportSettings;
exportSettings.exportFilename = m_exportFile();
exportSettings.exportKeyword = m_exportKeyword();
exportSettings.visibleActiveCellsValue = m_visibleActiveCellsValue();
exportSettings.hiddenActiveCellsValue = m_hiddenActiveCellsValue();
exportSettings.inactiveCellsValue = m_inactiveCellsValue();
if ( auto result = RicSaveEclipseInputVisibleCellsFeature::exportVisibleCells( view, exportSettings ); !result )
{
return std::unexpected( result.error() );
}
return nullptr;
}
@@ -18,6 +18,9 @@
#pragma once
#include "ExportCommands/RicSaveEclipseInputVisibleCellsUi.h"
#include "cafAppEnum.h"
#include "cafPdmField.h"
#include "cafPdmObjectHandle.h"
#include "cafPdmObjectMethod.h"
@@ -68,3 +71,30 @@ private:
caf::PdmField<QString> m_exportFile;
caf::PdmField<double> m_undefinedValue;
};
//==================================================================================================
/// Export a GRDECL keyword (FLUXNUM, MULTNUM or ACTNUM) with one value per cell based on the cell
/// visibility in the view at the current time step.
//==================================================================================================
class RimEclipseView_exportVisibleCells : public caf::PdmVoidObjectMethod
{
CAF_PDM_HEADER_INIT;
public:
RimEclipseView_exportVisibleCells( caf::PdmObjectHandle* self );
void setExportFile( const QString& exportFile );
void setExportKeyword( RicSaveEclipseInputVisibleCellsUi::ExportKeyword exportKeyword );
void setVisibleActiveCellsValue( int value );
void setHiddenActiveCellsValue( int value );
void setInactiveCellsValue( int value );
std::expected<caf::PdmObjectHandle*, QString> execute() override;
private:
caf::PdmField<QString> m_exportFile;
caf::PdmField<caf::AppEnum<RicSaveEclipseInputVisibleCellsUi::ExportKeyword>> m_exportKeyword;
caf::PdmField<int> m_visibleActiveCellsValue;
caf::PdmField<int> m_hiddenActiveCellsValue;
caf::PdmField<int> m_inactiveCellsValue;
};
@@ -303,3 +303,31 @@ def test_export_current_property(rips_instance, initialize_test):
with pytest.raises(rips.RipsError):
view.export_current_property()
def test_export_visible_cells(rips_instance, initialize_test):
case_path = dataroot.PATH + "/TEST10K_FLT_LGR_NNC/TEST10K_FLT_LGR_NNC.EGRID"
case = rips_instance.project.load_case(case_path)
view = case.create_view()
with tempfile.TemporaryDirectory(prefix="rips") as tmpdirname:
export_file = os.path.join(tmpdirname, "fluxnum.grdecl")
view.export_visible_cells(export_file=export_file)
assert os.path.exists(export_file)
with open(export_file) as f:
assert "FLUXNUM" in f.read(200)
export_file = os.path.join(tmpdirname, "actnum.grdecl")
view.export_visible_cells(
export_file=export_file,
export_keyword=rips.VisibleCellsExportKeyword.ACTNUM,
visible_active_cells_value=2,
)
with open(export_file) as f:
content = f.read()
assert "ACTNUM" in content[:200]
# Note: without a GUI no cells are visible, so the exported values are not asserted
values = content.split("ACTNUM")[1].split("/")[0].split()
assert len(values) > 0
with pytest.raises(rips.RipsError):
view.export_visible_cells()
-30
View File
@@ -129,36 +129,6 @@ def export_sim_well_fracture_completions(
)
@add_method(View)
def export_visible_cells(
self,
export_keyword="FLUXNUM",
visible_active_cells_value=1,
hidden_active_cells_value=0,
inactive_cells_value=0,
):
"""Export special properties for all visible cells.
Arguments:
export_keyword (string): The keyword to export.
Choices: 'FLUXNUM' or 'MULTNUM'. Default: 'FLUXNUM'
visible_active_cells_value (int): Value to export forvisible active cells. Default: 1
hidden_active_cells_value (int): Value to export for hidden active cells. Default: 0
inactive_cells_value (int): Value to export for inactive cells. Default: 0
"""
case_id = self.case().id
return self._execute_command(
exportVisibleCells=Cmd.ExportVisibleCellsRequest(
caseId=case_id,
viewId=self.id,
exportKeyword=export_keyword,
visibleActiveCellsValue=visible_active_cells_value,
hiddenActiveCellsValue=hidden_active_cells_value,
inactiveCellsValue=inactive_cells_value,
)
)
@add_method(View)
def export_property(self, undefined_value=0.0):
"""Export the current Eclipse property from the view