diff --git a/ApplicationLibCode/CommandFileInterface/RicfCommandForwarding.cpp b/ApplicationLibCode/CommandFileInterface/RicfCommandForwarding.cpp index fcb26942bb..6e83dd4fdc 100644 --- a/ApplicationLibCode/CommandFileInterface/RicfCommandForwarding.cpp +++ b/ApplicationLibCode/CommandFileInterface/RicfCommandForwarding.cpp @@ -30,6 +30,7 @@ #include "RimIdenticalGridCaseGroup.h" #include "RimOilField.h" #include "RimProject.h" +#include "RimWellLogPlot.h" #include "cafPdmObjectHandle.h" @@ -140,6 +141,22 @@ std::expected RicfForwarding::findStatistics return std::unexpected( QString( "Could not find statistics case with ID %1" ).arg( caseId ) ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::expected RicfForwarding::findWellLogPlot( int viewId ) +{ + RimProject* project = RimProject::current(); + if ( !project ) return std::unexpected( "No project is available." ); + + for ( RimWellLogPlot* plot : project->descendantsIncludingThisOfType() ) + { + if ( plot && plot->id() == viewId ) return plot; + } + + return std::unexpected( QString( "Could not find well log plot with ID %1" ).arg( viewId ) ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/CommandFileInterface/RicfCommandForwarding.h b/ApplicationLibCode/CommandFileInterface/RicfCommandForwarding.h index a28c06895a..97f08160f3 100644 --- a/ApplicationLibCode/CommandFileInterface/RicfCommandForwarding.h +++ b/ApplicationLibCode/CommandFileInterface/RicfCommandForwarding.h @@ -29,6 +29,7 @@ class Rim3dView; class RimFractureTemplate; class RimIdenticalGridCaseGroup; class RimEclipseStatisticsCase; +class RimWellLogPlot; namespace caf { @@ -57,6 +58,9 @@ std::expected findCaseGroup( int groupId ); /// Find a statistics case by case id in any grid case group. std::expected findStatisticsCase( int caseId ); +/// Find a well log plot by view id. +std::expected findWellLogPlot( int viewId ); + /// Find a fracture template by id. std::expected findFractureTemplate( int templateId ); diff --git a/ApplicationLibCode/CommandFileInterface/RicfExportWellLogPlotData.cpp b/ApplicationLibCode/CommandFileInterface/RicfExportWellLogPlotData.cpp index 7d6352cbe8..7e9c5c8557 100644 --- a/ApplicationLibCode/CommandFileInterface/RicfExportWellLogPlotData.cpp +++ b/ApplicationLibCode/CommandFileInterface/RicfExportWellLogPlotData.cpp @@ -17,20 +17,14 @@ ///////////////////////////////////////////////////////////////////////////////// #include "RicfExportWellLogPlotData.h" -#include "RiaLogging.h" +#include "RicfCommandForwarding.h" -#include "ExportCommands/RicExportToLasFileFeature.h" -#include "WellLogCommands/RicAsciiExportWellLogPlotFeature.h" - -#include "RimProject.h" #include "RimWellLogPlot.h" +#include "RimcDataContainerString.h" +#include "RimcWellLogPlot.h" #include "cafPdmFieldScriptingCapability.h" -#include -#include -#include - namespace caf { template <> @@ -75,59 +69,47 @@ RicfExportWellLogPlotData::RicfExportWellLogPlotData() //-------------------------------------------------------------------------------------------------- caf::PdmScriptResponse RicfExportWellLogPlotData::execute() { - QStringList errorMessages; + const QString commandName = classKeyword(); - caf::PdmScriptResponse response; - - if ( QFileInfo::exists( m_folder ) ) + auto plot = RicfForwarding::findWellLogPlot( m_viewId() ); + if ( !plot ) { - std::vector plots = RimProject::current()->descendantsIncludingThisOfType(); - RicfExportWellLogPlotDataResult* result = new RicfExportWellLogPlotDataResult; + // Legacy behavior: an unknown view id produced an empty result, not an error + caf::PdmScriptResponse response; + response.setResult( new RicfExportWellLogPlotDataResult ); + return response; + } - for ( RimWellLogPlot* plot : plots ) - { - if ( plot->id() == m_viewId() ) - { - if ( m_format() == ExportFormat::ASCII ) - { - QString validFileName = - RicAsciiExportWellLogPlotFeature::makeValidExportFileName( plot, m_folder(), m_filePrefix(), m_capitalizeFileNames() ); - if ( RicAsciiExportWellLogPlotFeature::exportAsciiForWellLogPlot( validFileName, plot ) ) - { - result->exportedFiles.v().push_back( validFileName ); - } - } - else - { - std::vector exportedFiles = RicExportToLasFileFeature::exportToLasFiles( m_folder(), - m_filePrefix(), - plot, - m_exportTvdRkb(), - m_capitalizeFileNames(), - true, - m_resampleInterval(), - m_convertCurveUnits() ); - if ( exportedFiles.empty() ) - { - errorMessages << QString( "No files exported for '%1'" ).arg( plot->description() ); - } - else - { - result->exportedFiles.v().insert( result->exportedFiles.v().end(), exportedFiles.begin(), exportedFiles.end() ); - } - } - } - } - response.setResult( result ); + std::expected result; + if ( m_format() == ExportFormat::ASCII ) + { + RimWellLogPlot_exportDataAsAscii method( plot.value() ); + method.setExportFolder( m_folder() ); + method.setFilePrefix( m_filePrefix() ); + method.setCapitalizeFileNames( m_capitalizeFileNames() ); + result = method.execute(); } else { - errorMessages << ( m_folder() + " does not exist" ); + RimWellLogPlot_exportDataAsLas method( plot.value() ); + method.setExportFolder( m_folder() ); + method.setFilePrefix( m_filePrefix() ); + method.setExportTvdRkb( m_exportTvdRkb() ); + method.setCapitalizeFileNames( m_capitalizeFileNames() ); + method.setResampleInterval( m_resampleInterval() ); + method.setConvertToStandardUnits( m_convertCurveUnits() ); + result = method.execute(); } - for ( QString errorMessage : errorMessages ) - { - response.updateStatus( caf::PdmScriptResponse::COMMAND_ERROR, errorMessage ); - } + if ( !result ) return RicfForwarding::errorResponse( result.error(), commandName ); + + auto* files = dynamic_cast( result.value() ); + + auto* exportResult = new RicfExportWellLogPlotDataResult; + if ( files ) exportResult->exportedFiles = files->m_stringValues(); + delete files; + + caf::PdmScriptResponse response; + response.setResult( exportResult ); return response; } diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcWellLogPlot.cpp b/ApplicationLibCode/ProjectDataModelCommands/RimcWellLogPlot.cpp index 0d8e8374df..45c2d65980 100644 --- a/ApplicationLibCode/ProjectDataModelCommands/RimcWellLogPlot.cpp +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcWellLogPlot.cpp @@ -19,6 +19,8 @@ #include "RiaApplication.h" +#include "ExportCommands/RicExportToLasFileFeature.h" +#include "WellLogCommands/RicAsciiExportWellLogPlotFeature.h" #include "WellLogCommands/RicNewWellLogPlotFeatureImpl.h" #include "RimEclipseCase.h" @@ -29,10 +31,13 @@ #include "RimWellLogPlotCollection.h" #include "RimWellLogTrack.h" #include "RimWellPath.h" +#include "RimcDataContainerString.h" #include "cafPdmAbstractFieldScriptingCapability.h" #include "cafPdmFieldScriptingCapability.h" +#include + CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellLogPlot, RimcWellLogPlot_newWellLogTrack, "NewWellLogTrack" ); //-------------------------------------------------------------------------------------------------- @@ -95,3 +100,186 @@ QString RimcWellLogPlot_newWellLogTrack::classKeywordReturnedType() const { return RimWellLogTrack::classKeywordStatic(); } + +CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellLogPlot, RimWellLogPlot_exportDataAsLas, "exportDataAsLas" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimWellLogPlot_exportDataAsLas::RimWellLogPlot_exportDataAsLas( caf::PdmObjectHandle* self ) + : caf::PdmObjectMethod( self, PdmObjectMethod::NullPointerType::NULL_IS_INVALID, PdmObjectMethod::ResultType::PERSISTENT_FALSE ) +{ + CAF_PDM_InitObject( "Export Data As LAS", "", "", "Export the curves of the plot to LAS files and return the exported file names" ); + + CAF_PDM_InitScriptableField( &m_exportFolder, "ExportFolder", QString(), "Export Folder", "", "", "Folder to write the LAS files to. Must exist." ); + CAF_PDM_InitScriptableField( &m_filePrefix, "FilePrefix", QString(), "File Prefix", "", "", "Prefix for the generated file names" ); + CAF_PDM_InitScriptableField( &m_exportTvdRkb, "ExportTvdRkb", false, "Export TVD RKB", "", "", "Export in TVD-RKB format" ); + CAF_PDM_InitScriptableField( &m_capitalizeFileNames, "CapitalizeFileNames", false, "Capitalize File Names", "", "", "Make all file names upper case" ); + CAF_PDM_InitScriptableField( &m_resampleInterval, + "ResampleInterval", + 0.0, + "Resample Interval", + "", + "", + "If > 0.0 the curves are resampled with this interval" ); + CAF_PDM_InitScriptableField( &m_convertToStandardUnits, + "ConvertToStandardUnits", + false, + "Convert To Standard Units", + "", + "", + "Convert curve units to standard units" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogPlot_exportDataAsLas::setExportFolder( const QString& exportFolder ) +{ + m_exportFolder = exportFolder; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogPlot_exportDataAsLas::setFilePrefix( const QString& filePrefix ) +{ + m_filePrefix = filePrefix; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogPlot_exportDataAsLas::setExportTvdRkb( bool enable ) +{ + m_exportTvdRkb = enable; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogPlot_exportDataAsLas::setCapitalizeFileNames( bool enable ) +{ + m_capitalizeFileNames = enable; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogPlot_exportDataAsLas::setResampleInterval( double interval ) +{ + m_resampleInterval = interval; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogPlot_exportDataAsLas::setConvertToStandardUnits( bool enable ) +{ + m_convertToStandardUnits = enable; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::expected RimWellLogPlot_exportDataAsLas::execute() +{ + auto* plot = self(); + if ( !plot ) return std::unexpected( "No well log plot is available." ); + + if ( m_exportFolder().isEmpty() ) return std::unexpected( "No export folder specified." ); + if ( !QFileInfo::exists( m_exportFolder() ) ) return std::unexpected( m_exportFolder() + " does not exist" ); + + const bool exportAllCurves = true; + std::vector exportedFiles = RicExportToLasFileFeature::exportToLasFiles( m_exportFolder(), + m_filePrefix(), + plot, + m_exportTvdRkb(), + m_capitalizeFileNames(), + exportAllCurves, + m_resampleInterval(), + m_convertToStandardUnits() ); + if ( exportedFiles.empty() ) return std::unexpected( QString( "No files exported for '%1'" ).arg( plot->description() ) ); + + auto* result = new RimcDataContainerString(); + result->m_stringValues = exportedFiles; + return result; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimWellLogPlot_exportDataAsLas::classKeywordReturnedType() const +{ + return RimcDataContainerString::classKeywordStatic(); +} + +CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellLogPlot, RimWellLogPlot_exportDataAsAscii, "exportDataAsAscii" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimWellLogPlot_exportDataAsAscii::RimWellLogPlot_exportDataAsAscii( caf::PdmObjectHandle* self ) + : caf::PdmObjectMethod( self, PdmObjectMethod::NullPointerType::NULL_IS_INVALID, PdmObjectMethod::ResultType::PERSISTENT_FALSE ) +{ + CAF_PDM_InitObject( "Export Data As ASCII", "", "", "Export the curves of the plot to a single ASCII file and return the file name" ); + + CAF_PDM_InitScriptableField( &m_exportFolder, "ExportFolder", QString(), "Export Folder", "", "", "Folder to write the file to. Must exist." ); + CAF_PDM_InitScriptableField( &m_filePrefix, "FilePrefix", QString(), "File Prefix", "", "", "Prefix for the generated file name" ); + CAF_PDM_InitScriptableField( &m_capitalizeFileNames, "CapitalizeFileNames", false, "Capitalize File Names", "", "", "Make the file name upper case" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogPlot_exportDataAsAscii::setExportFolder( const QString& exportFolder ) +{ + m_exportFolder = exportFolder; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogPlot_exportDataAsAscii::setFilePrefix( const QString& filePrefix ) +{ + m_filePrefix = filePrefix; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellLogPlot_exportDataAsAscii::setCapitalizeFileNames( bool enable ) +{ + m_capitalizeFileNames = enable; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::expected RimWellLogPlot_exportDataAsAscii::execute() +{ + auto* plot = self(); + if ( !plot ) return std::unexpected( "No well log plot is available." ); + + if ( m_exportFolder().isEmpty() ) return std::unexpected( "No export folder specified." ); + if ( !QFileInfo::exists( m_exportFolder() ) ) return std::unexpected( m_exportFolder() + " does not exist" ); + + QString fileName = + RicAsciiExportWellLogPlotFeature::makeValidExportFileName( plot, m_exportFolder(), m_filePrefix(), m_capitalizeFileNames() ); + if ( !RicAsciiExportWellLogPlotFeature::exportAsciiForWellLogPlot( fileName, plot ) ) + { + return std::unexpected( QString( "Could not export '%1' to %2" ).arg( plot->description() ).arg( fileName ) ); + } + + auto* result = new RimcDataContainerString(); + result->m_stringValues = { fileName }; + return result; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimWellLogPlot_exportDataAsAscii::classKeywordReturnedType() const +{ + return RimcDataContainerString::classKeywordStatic(); +} diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcWellLogPlot.h b/ApplicationLibCode/ProjectDataModelCommands/RimcWellLogPlot.h index 5cd1a0f4c6..4fb64ce50f 100644 --- a/ApplicationLibCode/ProjectDataModelCommands/RimcWellLogPlot.h +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcWellLogPlot.h @@ -50,3 +50,55 @@ private: caf::PdmPtrField m_case; caf::PdmPtrField m_wellPath; }; + +//================================================================================================== +/// Export the curves of the well log plot to LAS files. Returns the exported file names. +//================================================================================================== +class RimWellLogPlot_exportDataAsLas : public caf::PdmObjectMethod +{ + CAF_PDM_HEADER_INIT; + +public: + RimWellLogPlot_exportDataAsLas( caf::PdmObjectHandle* self ); + + void setExportFolder( const QString& exportFolder ); + void setFilePrefix( const QString& filePrefix ); + void setExportTvdRkb( bool enable ); + void setCapitalizeFileNames( bool enable ); + void setResampleInterval( double interval ); + void setConvertToStandardUnits( bool enable ); + + std::expected execute() override; + QString classKeywordReturnedType() const override; + +private: + caf::PdmField m_exportFolder; + caf::PdmField m_filePrefix; + caf::PdmField m_exportTvdRkb; + caf::PdmField m_capitalizeFileNames; + caf::PdmField m_resampleInterval; + caf::PdmField m_convertToStandardUnits; +}; + +//================================================================================================== +/// Export the curves of the well log plot to a single ASCII file. Returns the exported file name. +//================================================================================================== +class RimWellLogPlot_exportDataAsAscii : public caf::PdmObjectMethod +{ + CAF_PDM_HEADER_INIT; + +public: + RimWellLogPlot_exportDataAsAscii( caf::PdmObjectHandle* self ); + + void setExportFolder( const QString& exportFolder ); + void setFilePrefix( const QString& filePrefix ); + void setCapitalizeFileNames( bool enable ); + + std::expected execute() override; + QString classKeywordReturnedType() const override; + +private: + caf::PdmField m_exportFolder; + caf::PdmField m_filePrefix; + caf::PdmField m_capitalizeFileNames; +}; diff --git a/GrpcInterface/Python/rips/tests/test_well_log_extraction.py b/GrpcInterface/Python/rips/tests/test_well_log_extraction.py index 0eba3b66fb..a5ef977e2c 100644 --- a/GrpcInterface/Python/rips/tests/test_well_log_extraction.py +++ b/GrpcInterface/Python/rips/tests/test_well_log_extraction.py @@ -1,5 +1,6 @@ import sys import os +import pytest import tempfile sys.path.insert(1, os.path.join(sys.path[0], "../../")) @@ -46,3 +47,29 @@ def test_10k_well_log_extraction(rips_instance, initialize_test): with tempfile.TemporaryDirectory(prefix="rips") as tmpdirname: well_log_plot.export_data_as_las(export_folder=tmpdirname) + + +def test_export_data_as_ascii(rips_instance, initialize_test): + project = rips_instance.project.open( + dataroot.PATH + "/TEST10K_FLT_LGR_NNC/10KWithWellLog.rsp" + ) + well_log_plots = [ + plot for plot in project.plots() if isinstance(plot, rips.WellLogPlot) + ] + assert len(well_log_plots) > 0 + + with tempfile.TemporaryDirectory(prefix="rips") as tmpdirname: + files = well_log_plots[0].export_data_as_ascii( + export_folder=tmpdirname, file_prefix="pfx_" + ) + assert len(files) == 1 + assert os.path.exists(files[0]) + assert os.path.basename(files[0]).startswith("pfx_") + + las_files = well_log_plots[0].export_data_as_las(export_folder=tmpdirname) + assert len(las_files) >= 1 + for las in las_files: + assert las.endswith(".las") + + with pytest.raises(rips.RipsError): + well_log_plots[0].export_data_as_las(export_folder="/does/not/exist") diff --git a/GrpcInterface/Python/rips/well_log_plot.py b/GrpcInterface/Python/rips/well_log_plot.py index 20a1fab02b..4b7617e426 100644 --- a/GrpcInterface/Python/rips/well_log_plot.py +++ b/GrpcInterface/Python/rips/well_log_plot.py @@ -2,7 +2,7 @@ ResInsight Well Log Plot plot module """ -import Commands_pb2 +import warnings from .pdmobject import add_method from .resinsight_classes import WellLogPlot @@ -19,32 +19,44 @@ def export_data_as_las( capitalize_file_names: bool = False, resample_interval: float = 0.0, convert_to_standard_units: bool = False, + export_tvd_rkb: bool = None, ) -> List[str]: """Export LAS file(s) for the current plot Arguments: - export_folder(str): The path to export to. By default will use the global export folder + export_folder(str): The path to export to. Must exist. file_prefix (str): Exported file name prefix - export_tvdrkb(bool): Export in TVD-RKB format + export_tvdrkb(bool): Deprecated spelling of export_tvd_rkb capitalize_file_names(bool): Make all file names upper case resample_interval(double): if > 0.0 the files will be resampled + convert_to_standard_units(bool): Convert curve units to standard units + export_tvd_rkb(bool): Export in TVD-RKB format Returns: A list of files exported """ - res = self._execute_command( - exportWellLogPlotData=Commands_pb2.ExportWellLogPlotDataRequest( - exportFormat="LAS", - viewId=self.id, - exportFolder=export_folder, - filePrefix=file_prefix, - exportTvdRkb=export_tvdrkb, - capitalizeFileNames=capitalize_file_names, - resampleInterval=resample_interval, - convertCurveUnits=convert_to_standard_units, + if export_tvdrkb: + warnings.warn( + "WellLogPlot.export_data_as_las(export_tvdrkb=...) is deprecated, use export_tvd_rkb=...", + DeprecationWarning, + stacklevel=3, ) + if export_tvd_rkb is None: + export_tvd_rkb = export_tvdrkb + + from .resinsight_classes import DataContainerString + + res = self._call_pdm_method_return_value( + "exportDataAsLas", + DataContainerString, + export_folder=export_folder, + file_prefix=file_prefix, + export_tvd_rkb=export_tvd_rkb, + capitalize_file_names=capitalize_file_names, + resample_interval=resample_interval, + convert_to_standard_units=convert_to_standard_units, ) - return res.exportWellLogPlotDataResult.exportedFiles + return list(res.values) @add_method(WellLogPlot) @@ -54,25 +66,23 @@ def export_data_as_ascii( file_prefix: str = "", capitalize_file_names: bool = False, ) -> List[str]: - """Export LAS file(s) for the current plot + """Export ASCII file for the current plot Arguments: - export_folder(str): The path to export to. By default will use the global export folder + export_folder(str): The path to export to. Must exist. file_prefix (str): Exported file name prefix capitalize_file_names(bool): Make all file names upper case Returns: A list of files exported """ - res = self._execute_command( - exportWellLogPlotData=Commands_pb2.ExportWellLogPlotDataRequest( - exportFormat="ASCII", - viewId=self.id, - exportFolder=export_folder, - filePrefix=file_prefix, - exportTvdRkb=False, - capitalizeFileNames=capitalize_file_names, - resampleInterval=0.0, - ) + from .resinsight_classes import DataContainerString + + res = self._call_pdm_method_return_value( + "exportDataAsAscii", + DataContainerString, + export_folder=export_folder, + file_prefix=file_prefix, + capitalize_file_names=capitalize_file_names, ) - return res.exportWellLogPlotDataResult.exportedFiles + return list(res.values)