#14772 Python: Add Reservoir.export_property as object method and forward exportProperty to it

RimEclipseCase_exportProperty writes a cell property to a GRDECL style
text file and requires an explicit export file. RicfExportProperty
resolves the default file name from the command file executor's
PROPERTIES export folder and forwards to it.

The hand-written Case.export_property() wrapper is removed. The
generated Reservoir.export_property() has the same argument names and
shadows the base class wrapper anyway, so a deprecation shim would
never be reached.
This commit is contained in:
Kristian Bendiksen committed 2026-09-22 14:19:23 +02:00
1 parent 77c32a3fa5
commit d2078613a8
5 files changed
+185 -95

No files matched your search

@@ -18,23 +18,11 @@
#include "RicfExportProperty.h"
#include "RiaLogging.h"
#include "ExportCommands/RicEclipseCellResultToFileImpl.h"
#include "RicfApplicationTools.h"
#include "RicfCommandFileExecutor.h"
#include "RifEclipseInputFileTools.h"
#include "RigCaseCellResultsData.h"
#include "RigEclipseCaseData.h"
#include "RigEclipseResultAddress.h"
#include "RicfCommandForwarding.h"
#include "RimEclipseCase.h"
#include "RimEclipseCaseCollection.h"
#include "RimEclipseCellColors.h"
#include "RimEclipseView.h"
#include "RimProject.h"
#include "RimcEclipseCase.h"
#include "cafPdmFieldScriptingCapability.h"
#include "cafUtils.h"
@@ -61,67 +49,33 @@ RicfExportProperty::RicfExportProperty()
//--------------------------------------------------------------------------------------------------
caf::PdmScriptResponse RicfExportProperty::execute()
{
using TOOLS = RicfApplicationTools;
const QString commandName = classKeyword();
RimEclipseCase* eclipseCase = TOOLS::caseFromId( m_caseId() );
auto rimCase = RicfForwarding::findCase( m_caseId() );
if ( !rimCase ) return RicfForwarding::errorResponse( rimCase.error(), commandName );
auto* eclipseCase = dynamic_cast<RimEclipseCase*>( rimCase.value() );
if ( !eclipseCase )
{
if ( !eclipseCase )
{
QString error = QString( "exportProperty: Could not find case with ID %1" ).arg( m_caseId() );
RiaLogging::error( error.toStdString() );
return caf::PdmScriptResponse( caf::PdmScriptResponse::COMMAND_ERROR, error );
}
if ( !eclipseCase->eclipseCaseData() )
{
if ( !eclipseCase->openReservoirCase() )
{
QString error = QString( "exportProperty: Could not find eclipseCaseData 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 );
}
RigEclipseCaseData* eclipseCaseData = eclipseCase->eclipseCaseData();
RigCaseCellResultsData* cellResultsData = eclipseCaseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
if ( !cellResultsData->ensureKnownResultLoaded( RigEclipseResultAddress( m_propertyName ) ) )
{
QString error = QString( "exportProperty: Could not find result property : %1" ).arg( m_propertyName() );
RiaLogging::error( error.toStdString() );
return caf::PdmScriptResponse( caf::PdmScriptResponse::COMMAND_ERROR, error );
}
QString filePath = m_exportFileName;
// Resolve the default export file from the command file executor state. The Rimc method requires an explicit file.
QString filePath = m_exportFileName();
if ( filePath.isNull() )
{
QDir propertiesDir( RicfCommandFileExecutor::instance()->getExportPath( RicfCommandFileExecutor::ExportType::PROPERTIES ) );
QString fileName = QString( "%1-%2" ).arg( eclipseCase->caseUserDescription() ).arg( m_propertyName );
QString fileName = QString( "%1-%2" ).arg( eclipseCase->caseUserDescription() ).arg( m_propertyName() );
fileName = caf::Utils::makeValidFileBasename( fileName );
filePath = propertiesDir.filePath( fileName );
}
QString eclipseKeyword = m_eclipseKeyword;
if ( eclipseKeyword.isNull() )
{
eclipseKeyword = m_propertyName;
}
RimEclipseCase_exportProperty method( eclipseCase );
method.setTimeStep( m_timeStepIndex() );
method.setPropertyName( m_propertyName() );
method.setEclipseKeyword( m_eclipseKeyword() );
method.setUndefinedValue( m_undefinedValue() );
method.setExportFile( filePath );
bool writeEchoKeywords = false;
QString errMsg;
if ( !RicEclipseCellResultToFileImpl::writePropertyToTextFile( filePath,
eclipseCase->eclipseCaseData(),
m_timeStepIndex,
m_propertyName,
eclipseKeyword,
m_undefinedValue,
writeEchoKeywords,
&errMsg ) )
{
return caf::PdmScriptResponse( caf::PdmScriptResponse::COMMAND_ERROR, errMsg );
}
return caf::PdmScriptResponse();
return RicfForwarding::toScriptResponse( method.execute(), commandName );
}
@@ -22,6 +22,8 @@
#include "RiaGuiApplication.h"
#include "RiaKeyValueStoreUtil.h"
#include "ExportCommands/RicEclipseCellResultToFileImpl.h"
#include "RifInputPropertyLoader.h"
#include "RigActiveCellInfo.h"
@@ -450,3 +452,110 @@ std::expected<RiaDefines::ResultDataType, QString> RimcEclipseCase_propertyDataT
return std::unexpected( QString( "Property not found: %1" ).arg( m_propertyName() ) );
}
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimEclipseCase, RimEclipseCase_exportProperty, "exportProperty" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEclipseCase_exportProperty::RimEclipseCase_exportProperty( caf::PdmObjectHandle* self )
: caf::PdmVoidObjectMethod( self )
{
CAF_PDM_InitObject( "Export Property", "", "", "Export a cell property of the case to a GRDECL style text file" );
CAF_PDM_InitScriptableField( &m_timeStep, "TimeStep", -1, "Time Step", "", "", "Zero-based time step index. Ignored for static properties." );
CAF_PDM_InitScriptableField( &m_propertyName, "PropertyName", QString(), "Property Name", "", "", "Name of the property to export" );
CAF_PDM_InitScriptableField( &m_eclipseKeyword,
"EclipseKeyword",
QString(),
"Eclipse Keyword",
"",
"",
"Keyword written to the file header. Defaults to the property name." );
CAF_PDM_InitScriptableField( &m_undefinedValue, "UndefinedValue", 0.0, "Undefined Value", "", "", "Value written for undefined cells" );
CAF_PDM_InitScriptableField( &m_exportFile, "ExportFile", QString(), "Export File", "", "", "Full path of the file to write" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseCase_exportProperty::setTimeStep( int timeStep )
{
m_timeStep = timeStep;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseCase_exportProperty::setPropertyName( const QString& propertyName )
{
m_propertyName = propertyName;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseCase_exportProperty::setEclipseKeyword( const QString& eclipseKeyword )
{
m_eclipseKeyword = eclipseKeyword;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseCase_exportProperty::setUndefinedValue( double undefinedValue )
{
m_undefinedValue = undefinedValue;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEclipseCase_exportProperty::setExportFile( const QString& exportFile )
{
m_exportFile = exportFile;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<caf::PdmObjectHandle*, QString> RimEclipseCase_exportProperty::execute()
{
auto* eclipseCase = self<RimEclipseCase>();
if ( !eclipseCase ) return std::unexpected( "No case is available." );
if ( m_propertyName().isEmpty() ) return std::unexpected( "No property name specified." );
if ( m_exportFile().isEmpty() ) return std::unexpected( "No export file specified." );
if ( !eclipseCase->eclipseCaseData() && !eclipseCase->openReservoirCase() )
{
return std::unexpected( QString( "Could not open case '%1'" ).arg( eclipseCase->caseUserDescription() ) );
}
RigEclipseCaseData* eclipseCaseData = eclipseCase->eclipseCaseData();
RigCaseCellResultsData* cellResultsData = eclipseCaseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
if ( !cellResultsData->ensureKnownResultLoaded( RigEclipseResultAddress( m_propertyName() ) ) )
{
return std::unexpected( QString( "Could not find result property '%1'" ).arg( m_propertyName() ) );
}
QString eclipseKeyword = m_eclipseKeyword();
if ( eclipseKeyword.isEmpty() ) eclipseKeyword = m_propertyName();
const bool writeEchoKeywords = false;
QString errorMessage;
if ( !RicEclipseCellResultToFileImpl::writePropertyToTextFile( m_exportFile(),
eclipseCaseData,
m_timeStep(),
m_propertyName(),
eclipseKeyword,
m_undefinedValue(),
writeEchoKeywords,
&errorMessage ) )
{
return std::unexpected( errorMessage );
}
return nullptr;
}
@@ -161,3 +161,29 @@ private:
caf::PdmField<int> m_timeStep;
caf::PdmField<int> m_gridIndex;
};
//==================================================================================================
/// Export a cell property (static or dynamic, matrix model) of the case to a GRDECL style text file.
//==================================================================================================
class RimEclipseCase_exportProperty : public caf::PdmVoidObjectMethod
{
CAF_PDM_HEADER_INIT;
public:
RimEclipseCase_exportProperty( caf::PdmObjectHandle* self );
void setTimeStep( int timeStep );
void setPropertyName( const QString& propertyName );
void setEclipseKeyword( const QString& eclipseKeyword );
void setUndefinedValue( double undefinedValue );
void setExportFile( const QString& exportFile );
std::expected<caf::PdmObjectHandle*, QString> execute() override;
private:
caf::PdmField<int> m_timeStep;
caf::PdmField<QString> m_propertyName;
caf::PdmField<QString> m_eclipseKeyword;
caf::PdmField<double> m_undefinedValue;
caf::PdmField<QString> m_exportFile;
};
-30
View File
@@ -970,36 +970,6 @@ def set_grid_property(
raise IndexError
@add_method(Case)
def export_property(
self,
time_step: int,
property_name: str,
eclipse_keyword: Any = property,
undefined_value: float = 0.0,
export_file: Any = property,
) -> Any:
"""Export an Eclipse property
Arguments:
time_step (int): time step index
property_name (str): property to export
eclipse_keyword (str): Keyword used in export header. Defaults: value of property
undefined_value (double): Value to use for undefined values. Defaults to 0.0
export_file (str): File name for export. Defaults to the value of property parameter
"""
return self._execute_command(
exportProperty=Cmd.ExportPropertyRequest(
caseId=self.id,
timeStep=time_step,
property=property_name,
eclipseKeyword=eclipse_keyword,
undefinedValue=undefined_value,
exportFile=export_file,
)
)
@add_method(Case)
def create_well_bore_stability_plot(
self,
@@ -256,3 +256,34 @@ def test_exportPropertyInView(rips_instance, initialize_test):
expected_file_name = case.name + "-" + str("3D_View") + "-" + "T0" + "-SOIL"
full_path = tmpdirname + "/" + expected_file_name
assert os.path.exists(full_path)
def test_export_property(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)
with tempfile.TemporaryDirectory(prefix="rips") as tmpdirname:
export_file = os.path.join(tmpdirname, "soil_t1.grdecl")
case.export_property(time_step=1, property_name="SOIL", export_file=export_file)
assert os.path.exists(export_file)
with open(export_file) as f:
assert "SOIL" in f.read(200)
# Keyword override and static property
export_file = os.path.join(tmpdirname, "poro.grdecl")
case.export_property(
time_step=0,
property_name="PORO",
eclipse_keyword="MYPORO",
export_file=export_file,
)
with open(export_file) as f:
assert "MYPORO" in f.read(200)
with pytest.raises(rips.RipsError):
case.export_property(
time_step=0,
property_name="NOT_A_PROPERTY",
export_file=os.path.join(tmpdirname, "x"),
)
with pytest.raises(rips.RipsError):
case.export_property(time_step=0, property_name="PORO")