diff --git a/ApplicationLibCode/CommandFileInterface/RicfCreateLgrForCompletions.cpp b/ApplicationLibCode/CommandFileInterface/RicfCreateLgrForCompletions.cpp index dba4f2dd39..f8faf6dd7c 100644 --- a/ApplicationLibCode/CommandFileInterface/RicfCreateLgrForCompletions.cpp +++ b/ApplicationLibCode/CommandFileInterface/RicfCreateLgrForCompletions.cpp @@ -18,27 +18,13 @@ #include "RicfCreateLgrForCompletions.h" -#include "RiaLogging.h" -#include "RiaWellNameComparer.h" - #include "RicfApplicationTools.h" -#include "RicfCommandFileExecutor.h" +#include "RicfCommandForwarding.h" -#include "ExportCommands/RicExportLgrFeature.h" -#include "RicCreateTemporaryLgrFeature.h" -#include "RicDeleteTemporaryLgrsFeature.h" - -#include "RigReservoirGridTools.h" - -#include "RimDialogData.h" #include "RimEclipseCase.h" -#include "RimEclipseCaseCollection.h" -#include "RimFractureTemplate.h" -#include "RimOilField.h" -#include "RimProject.h" #include "RimWellPath.h" +#include "RimcEclipseCase.h" -#include "cafCmdFeatureManager.h" #include "cafPdmFieldScriptingCapability.h" #include @@ -64,75 +50,41 @@ RicfCreateLgrForCompletions::RicfCreateLgrForCompletions() //-------------------------------------------------------------------------------------------------- caf::PdmScriptResponse RicfCreateLgrForCompletions::execute() { - using TOOLS = RicfApplicationTools; + const QString commandName = classKeyword(); - std::vector wellPaths; - - // Find well paths + QStringList wellsNotFound; + std::vector wellPaths = + RicfApplicationTools::wellPathsFromNames( RicfApplicationTools::toQStringList( m_wellPathNames ), &wellsNotFound ); + if ( !wellsNotFound.empty() ) { - QStringList wellsNotFound; - wellPaths = TOOLS::wellPathsFromNames( TOOLS::toQStringList( m_wellPathNames ), &wellsNotFound ); - if ( !wellsNotFound.empty() ) - { - QString error = QString( "createLgrForCompletions: These well paths were not found: " ) + wellsNotFound.join( ", " ); - RiaLogging::error( error.toStdString() ); - return caf::PdmScriptResponse( caf::PdmScriptResponse::COMMAND_ERROR, error ); - } + return RicfForwarding::errorResponse( "These well paths were not found: " + wellsNotFound.join( ", " ), commandName ); + } + if ( wellPaths.empty() ) return RicfForwarding::errorResponse( "No well paths found", commandName ); + + auto rimCase = RicfForwarding::findCase( m_caseId() ); + if ( !rimCase ) return RicfForwarding::errorResponse( rimCase.error(), commandName ); + + auto* eclipseCase = dynamic_cast( rimCase.value() ); + if ( !eclipseCase ) + { + return RicfForwarding::errorResponse( QString( "Case with ID %1 is not an Eclipse case" ).arg( m_caseId() ), commandName ); } - if ( wellPaths.empty() ) - { - QString error( "No well paths found" ); - RiaLogging::error( error.toStdString() ); - return caf::PdmScriptResponse( caf::PdmScriptResponse::COMMAND_ERROR, error ); - } + RimEclipseCase_createLgrForCompletions method( eclipseCase ); + method.setWellPaths( wellPaths ); + method.setTimeStep( m_timeStep() ); + method.setRefinement( m_refinementI(), m_refinementJ(), m_refinementK() ); + method.setSplitType( m_splitType() ); - caf::CmdFeatureManager* commandManager = caf::CmdFeatureManager::instance(); - auto feature = dynamic_cast( commandManager->getCommandFeature( "RicCreateTemporaryLgrFeature" ) ); - - RimEclipseCase* eclipseCase = nullptr; - { - for ( RimEclipseCase* c : RimProject::current()->activeOilField()->analysisModels->cases() ) - { - if ( c->caseId() == m_caseId() ) - { - eclipseCase = c; - break; - } - } - if ( !eclipseCase ) - { - QString error( QString( "createLgrForCompletions: Could not find case with ID %1" ).arg( m_caseId() ) ); - RiaLogging::error( error.toStdString() ); - return caf::PdmScriptResponse( caf::PdmScriptResponse::COMMAND_ERROR, error ); - } - } - - RicDeleteTemporaryLgrsFeature::deleteAllTemporaryLgrs( eclipseCase ); - - cvf::Vec3st lgrCellCounts( m_refinementI, m_refinementJ, m_refinementK ); - QStringList wellsIntersectingOtherLgrs; - - feature->createLgrsForWellPaths( wellPaths, - eclipseCase, - m_timeStep, - lgrCellCounts, - m_splitType(), - { RigCompletionData::CompletionType::PERFORATION, - RigCompletionData::CompletionType::FRACTURE, - RigCompletionData::CompletionType::FISHBONES }, - &wellsIntersectingOtherLgrs ); - - RigReservoirGridTools::refreshEclipseCaseDataAndViews( eclipseCase ); + auto result = method.execute(); + if ( !result ) return RicfForwarding::errorResponse( result.error(), commandName ); caf::PdmScriptResponse response; - if ( !wellsIntersectingOtherLgrs.empty() ) + if ( !method.wellsIntersectingOtherLgrs().empty() ) { - auto wellsList = wellsIntersectingOtherLgrs.join( ", " ); - QString warning( "createLgrForCompletions: No LGRs created for some wells due to existing intersecting " - "LGR(s).Affected wells : " + - wellsList ); - RiaLogging::warning( warning.toStdString() ); + QString warning = QString( "%1: No LGRs created for some wells due to existing intersecting LGR(s). Affected wells: %2" ) + .arg( commandName ) + .arg( method.wellsIntersectingOtherLgrs().join( ", " ) ); response.updateStatus( caf::PdmScriptResponse::COMMAND_WARNING, warning ); } return response; diff --git a/ApplicationLibCode/Commands/ExportCommands/RicExportLgrUi.cpp b/ApplicationLibCode/Commands/ExportCommands/RicExportLgrUi.cpp index 0ba51f6312..3302c7db58 100644 --- a/ApplicationLibCode/Commands/ExportCommands/RicExportLgrUi.cpp +++ b/ApplicationLibCode/Commands/ExportCommands/RicExportLgrUi.cpp @@ -23,10 +23,21 @@ #include "RimEclipseCase.h" #include "RimTools.h" +#include "cafPdmScriptEnumNameRegistry.h" #include "cafPdmUiFilePathEditor.h" #include "cafPdmUiTextEditor.h" #include "cafVecIjk.h" +namespace +{ +// Pin the Python StrEnum class name for the LGR split type used by RimEclipseCase_createLgrForCompletions +struct RegisterScriptEnumNames +{ + RegisterScriptEnumNames() { caf::PdmScriptEnumNameRegistry::registerName( "LgrSplitType" ); } +}; +const RegisterScriptEnumNames s_registerScriptEnumNames; +} // namespace + CAF_PDM_SOURCE_INIT( RicExportLgrUi, "RicExportLgrUi" ); //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcEclipseCase.cpp b/ApplicationLibCode/ProjectDataModelCommands/RimcEclipseCase.cpp index 5a9c0d6e9f..f22c7fb327 100644 --- a/ApplicationLibCode/ProjectDataModelCommands/RimcEclipseCase.cpp +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcEclipseCase.cpp @@ -21,10 +21,14 @@ #include "RiaApplication.h" #include "RiaGuiApplication.h" #include "RiaKeyValueStoreUtil.h" +#include "RiaLogging.h" #include "CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.h" #include "CompletionExportCommands/RicWellPathExportMswCompletionsImpl.h" #include "ExportCommands/RicEclipseCellResultToFileImpl.h" +#include "ExportCommands/RicExportLgrFeature.h" +#include "RicCreateTemporaryLgrFeature.h" +#include "RicDeleteTemporaryLgrsFeature.h" #include "RifInputPropertyLoader.h" @@ -37,6 +41,7 @@ #include "RigMainGrid.h" #include "RigNoRefinement.h" #include "RigResdataGridConverter.h" +#include "RigReservoirGridTools.h" #include "RigResultAccessor.h" #include "RigResultAccessorFactory.h" @@ -52,6 +57,7 @@ #include "RimcDataContainerString.h" +#include "cafCmdFeatureManager.h" #include "cafPdmFieldScriptingCapability.h" #include "cvfArray.h" @@ -936,3 +942,227 @@ std::expected RimEclipseCase_exportMswCompletion return nullptr; } + +CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimEclipseCase, RimEclipseCase_createLgrForCompletions, "createLgrForCompletions" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimEclipseCase_createLgrForCompletions::RimEclipseCase_createLgrForCompletions( caf::PdmObjectHandle* self ) + : caf::PdmVoidObjectMethod( self ) +{ + CAF_PDM_InitObject( "Create LGR for Completions", + "", + "", + "Create temporary local grid refinements around the completions of the given well paths" ); + + CAF_PDM_InitScriptableFieldNoDefault( &m_wellPaths, "WellPaths", "Well Paths", "", "", "Well paths to create LGRs for" ); + CAF_PDM_InitScriptableField( &m_timeStep, "TimeStep", 0, "Time Step", "", "", "Zero-based time step index" ); + CAF_PDM_InitScriptableField( &m_refinementI, "RefinementI", 1, "Refinement I", "", "", "Number of refined cells in I direction" ); + CAF_PDM_InitScriptableField( &m_refinementJ, "RefinementJ", 1, "Refinement J", "", "", "Number of refined cells in J direction" ); + CAF_PDM_InitScriptableField( &m_refinementK, "RefinementK", 1, "Refinement K", "", "", "Number of refined cells in K direction" ); + CAF_PDM_InitScriptableField( &m_splitType, "SplitType", Lgr::SplitTypeEnum(), "Split Type", "", "", "Defines how to split the LGRs" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEclipseCase_createLgrForCompletions::setWellPaths( const std::vector& wellPaths ) +{ + m_wellPaths.setValue( wellPaths ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEclipseCase_createLgrForCompletions::setTimeStep( int timeStep ) +{ + m_timeStep = timeStep; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEclipseCase_createLgrForCompletions::setRefinement( int refinementI, int refinementJ, int refinementK ) +{ + m_refinementI = refinementI; + m_refinementJ = refinementJ; + m_refinementK = refinementK; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEclipseCase_createLgrForCompletions::setSplitType( Lgr::SplitType splitType ) +{ + m_splitType = splitType; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QStringList RimEclipseCase_createLgrForCompletions::wellsIntersectingOtherLgrs() const +{ + return m_wellsIntersectingOtherLgrs; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::expected RimEclipseCase_createLgrForCompletions::execute() +{ + auto* eclipseCase = self(); + if ( !eclipseCase ) return std::unexpected( "No case is available." ); + + std::vector wellPaths = m_wellPaths.ptrReferencedObjectsByType(); + if ( wellPaths.empty() ) return std::unexpected( "No well paths specified." ); + + if ( m_refinementI() < 1 || m_refinementJ() < 1 || m_refinementK() < 1 ) + { + return std::unexpected( "Refinement must be at least 1 in all directions." ); + } + + auto* feature = dynamic_cast( + caf::CmdFeatureManager::instance()->getCommandFeature( "RicCreateTemporaryLgrFeature" ) ); + if ( !feature ) return std::unexpected( "The create LGR feature is not available." ); + + RicDeleteTemporaryLgrsFeature::deleteAllTemporaryLgrs( eclipseCase ); + + cvf::Vec3st lgrCellCounts( m_refinementI(), m_refinementJ(), m_refinementK() ); + m_wellsIntersectingOtherLgrs.clear(); + + feature->createLgrsForWellPaths( wellPaths, + eclipseCase, + m_timeStep(), + lgrCellCounts, + m_splitType(), + { RigCompletionData::CompletionType::PERFORATION, + RigCompletionData::CompletionType::FRACTURE, + RigCompletionData::CompletionType::FISHBONES }, + &m_wellsIntersectingOtherLgrs ); + + RigReservoirGridTools::refreshEclipseCaseDataAndViews( eclipseCase ); + + if ( !m_wellsIntersectingOtherLgrs.empty() ) + { + RiaLogging::warning( QString( "No LGRs created for some wells due to existing intersecting LGR(s). Affected wells: %1" ) + .arg( m_wellsIntersectingOtherLgrs.join( ", " ) ) + .toStdString() ); + } + + return nullptr; +} + +CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimEclipseCase, RimEclipseCase_exportLgrForCompletions, "exportLgrForCompletions" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimEclipseCase_exportLgrForCompletions::RimEclipseCase_exportLgrForCompletions( caf::PdmObjectHandle* self ) + : caf::PdmVoidObjectMethod( self ) +{ + CAF_PDM_InitObject( "Export LGR for Completions", + "", + "", + "Export local grid refinements around the completions of the given well paths to CARFIN files" ); + + CAF_PDM_InitScriptableFieldNoDefault( &m_wellPaths, "WellPaths", "Well Paths", "", "", "Well paths to export LGRs for" ); + CAF_PDM_InitScriptableField( &m_timeStep, "TimeStep", 0, "Time Step", "", "", "Zero-based time step index" ); + CAF_PDM_InitScriptableField( &m_exportFolder, "ExportFolder", QString(), "Export Folder", "", "", "Folder to write the export files to" ); + CAF_PDM_InitScriptableField( &m_refinementI, "RefinementI", 1, "Refinement I", "", "", "Number of refined cells in I direction" ); + CAF_PDM_InitScriptableField( &m_refinementJ, "RefinementJ", 1, "Refinement J", "", "", "Number of refined cells in J direction" ); + CAF_PDM_InitScriptableField( &m_refinementK, "RefinementK", 1, "Refinement K", "", "", "Number of refined cells in K direction" ); + CAF_PDM_InitScriptableField( &m_splitType, "SplitType", Lgr::SplitTypeEnum(), "Split Type", "", "", "Defines how to split the LGRs" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEclipseCase_exportLgrForCompletions::setWellPaths( const std::vector& wellPaths ) +{ + m_wellPaths.setValue( wellPaths ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEclipseCase_exportLgrForCompletions::setTimeStep( int timeStep ) +{ + m_timeStep = timeStep; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEclipseCase_exportLgrForCompletions::setExportFolder( const QString& exportFolder ) +{ + m_exportFolder = exportFolder; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEclipseCase_exportLgrForCompletions::setRefinement( int refinementI, int refinementJ, int refinementK ) +{ + m_refinementI = refinementI; + m_refinementJ = refinementJ; + m_refinementK = refinementK; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEclipseCase_exportLgrForCompletions::setSplitType( Lgr::SplitType splitType ) +{ + m_splitType = splitType; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QStringList RimEclipseCase_exportLgrForCompletions::wellsIntersectingOtherLgrs() const +{ + return m_wellsIntersectingOtherLgrs; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::expected RimEclipseCase_exportLgrForCompletions::execute() +{ + auto* eclipseCase = self(); + if ( !eclipseCase ) return std::unexpected( "No case is available." ); + + std::vector wellPaths = m_wellPaths.ptrReferencedObjectsByType(); + if ( wellPaths.empty() ) return std::unexpected( "No well paths specified." ); + + if ( m_exportFolder().isEmpty() ) return std::unexpected( "No export folder specified." ); + + if ( m_refinementI() < 1 || m_refinementJ() < 1 || m_refinementK() < 1 ) + { + return std::unexpected( "Refinement must be at least 1 in all directions." ); + } + + cvf::Vec3st lgrCellCounts( m_refinementI(), m_refinementJ(), m_refinementK() ); + m_wellsIntersectingOtherLgrs.clear(); + + RicExportLgrFeature::exportLgrsForWellPaths( m_exportFolder(), + wellPaths, + eclipseCase, + m_timeStep(), + lgrCellCounts, + m_splitType(), + { RigCompletionData::CompletionType::PERFORATION, + RigCompletionData::CompletionType::FRACTURE, + RigCompletionData::CompletionType::FISHBONES }, + &m_wellsIntersectingOtherLgrs ); + + if ( !m_wellsIntersectingOtherLgrs.empty() ) + { + RiaLogging::warning( QString( "No export for some wells due to existing intersecting LGR(s). Affected wells: %1" ) + .arg( m_wellsIntersectingOtherLgrs.join( ", " ) ) + .toStdString() ); + } + + return nullptr; +} diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcEclipseCase.h b/ApplicationLibCode/ProjectDataModelCommands/RimcEclipseCase.h index 801cb31a11..235fb11dd9 100644 --- a/ApplicationLibCode/ProjectDataModelCommands/RimcEclipseCase.h +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcEclipseCase.h @@ -22,6 +22,7 @@ #include "RiaPorosityModel.h" #include "CompletionExportCommands/RicExportCompletionDataSettingsUi.h" +#include "ExportCommands/RicLgrSplitType.h" #include "cafAppEnum.h" #include "cafPdmField.h" @@ -275,3 +276,68 @@ private: caf::PdmField m_includeFishbones; caf::PdmField m_includeFractures; }; + +//================================================================================================== +/// Create temporary local grid refinements (LGRs) around the completions of the given well paths. +/// Existing temporary LGRs in the case are deleted first. +//================================================================================================== +class RimEclipseCase_createLgrForCompletions : public caf::PdmVoidObjectMethod +{ + CAF_PDM_HEADER_INIT; + +public: + RimEclipseCase_createLgrForCompletions( caf::PdmObjectHandle* self ); + + void setWellPaths( const std::vector& wellPaths ); + void setTimeStep( int timeStep ); + void setRefinement( int refinementI, int refinementJ, int refinementK ); + void setSplitType( Lgr::SplitType splitType ); + + std::expected execute() override; + + /// Wells skipped because they intersect existing LGRs. Valid after execute(). + QStringList wellsIntersectingOtherLgrs() const; + +private: + caf::PdmPtrArrayField m_wellPaths; + caf::PdmField m_timeStep; + caf::PdmField m_refinementI; + caf::PdmField m_refinementJ; + caf::PdmField m_refinementK; + caf::PdmField m_splitType; + + QStringList m_wellsIntersectingOtherLgrs; +}; + +//================================================================================================== +/// Export local grid refinements (LGRs) around the completions of the given well paths to CARFIN files. +//================================================================================================== +class RimEclipseCase_exportLgrForCompletions : public caf::PdmVoidObjectMethod +{ + CAF_PDM_HEADER_INIT; + +public: + RimEclipseCase_exportLgrForCompletions( caf::PdmObjectHandle* self ); + + void setWellPaths( const std::vector& wellPaths ); + void setTimeStep( int timeStep ); + void setExportFolder( const QString& exportFolder ); + void setRefinement( int refinementI, int refinementJ, int refinementK ); + void setSplitType( Lgr::SplitType splitType ); + + std::expected execute() override; + + /// Wells skipped because they intersect existing LGRs. Valid after execute(). + QStringList wellsIntersectingOtherLgrs() const; + +private: + caf::PdmPtrArrayField m_wellPaths; + caf::PdmField m_timeStep; + caf::PdmField m_exportFolder; + caf::PdmField m_refinementI; + caf::PdmField m_refinementJ; + caf::PdmField m_refinementK; + caf::PdmField m_splitType; + + QStringList m_wellsIntersectingOtherLgrs; +}; diff --git a/GrpcInterface/Python/rips/case.py b/GrpcInterface/Python/rips/case.py index d38f9b01da..30c26acc37 100644 --- a/GrpcInterface/Python/rips/case.py +++ b/GrpcInterface/Python/rips/case.py @@ -584,7 +584,14 @@ def create_lgr_for_completion( "LGR_PER_COMPLETION" | One LGR for each completion (fracture, perforation, ...) "LGR_PER_WELL" | One LGR for each well + Deprecated: use Reservoir.create_lgr_for_completions(well_paths=[...], ...) instead, which takes + WellPath objects. """ + warnings.warn( + "Case.create_lgr_for_completion() is deprecated, use Reservoir.create_lgr_for_completions() instead", + DeprecationWarning, + stacklevel=3, + ) if isinstance(well_path_names, str): well_path_names = [well_path_names] return self._execute_command(