From 3b086f7737ba49f7c94bf6a82cf9a421f0a03a2d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 20:10:05 +0000 Subject: [PATCH 1/3] Initial plan From 5bece0fae15082e5d64b7f146be4f1d2a523072f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 20:21:57 +0000 Subject: [PATCH 2/3] #issue Add importSummaryEnsembleSumo Python API method for fmu-sumo integration Agent-Logs-Url: https://github.com/magnesj/ResInsight/sessions/3a967829-5337-492b-b130-d83837d37ea9 Co-authored-by: magnesj <1793152+magnesj@users.noreply.github.com> --- .../Cloud/RimCloudDataSourceCollection.cpp | 8 ++ .../Cloud/RimCloudDataSourceCollection.h | 1 + .../ProjectDataModelCommands/RimcProject.cpp | 66 ++++++++++ .../ProjectDataModelCommands/RimcProject.h | 21 ++++ .../sumo_ensemble_import.py | 116 ++++++++++++++++++ 5 files changed, 212 insertions(+) create mode 100644 GrpcInterface/Python/rips/PythonExamples/export_and_plotting/sumo_ensemble_import.py diff --git a/ApplicationLibCode/ProjectDataModel/Cloud/RimCloudDataSourceCollection.cpp b/ApplicationLibCode/ProjectDataModel/Cloud/RimCloudDataSourceCollection.cpp index 387dcb9a1c2..2bfc8afbd30 100644 --- a/ApplicationLibCode/ProjectDataModel/Cloud/RimCloudDataSourceCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/Cloud/RimCloudDataSourceCollection.cpp @@ -101,6 +101,14 @@ void RimCloudDataSourceCollection::createEnsemblesFromSelectedDataSources( const sumCaseMainColl->updateAllRequiredEditors(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimCloudDataSourceCollection::addSumoDataSource( RimSummarySumoDataSource* dataSource ) +{ + m_sumoDataSources.push_back( dataSource ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/Cloud/RimCloudDataSourceCollection.h b/ApplicationLibCode/ProjectDataModel/Cloud/RimCloudDataSourceCollection.h index a311c3c71ca..9b214e1e6c3 100644 --- a/ApplicationLibCode/ProjectDataModel/Cloud/RimCloudDataSourceCollection.h +++ b/ApplicationLibCode/ProjectDataModel/Cloud/RimCloudDataSourceCollection.h @@ -44,6 +44,7 @@ class RimCloudDataSourceCollection : public caf::PdmObject std::vector sumoDataSources() const; + void addSumoDataSource( RimSummarySumoDataSource* dataSource ); static void createEnsemblesFromSelectedDataSources( const std::vector& dataSources ); private: diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcProject.cpp b/ApplicationLibCode/ProjectDataModelCommands/RimcProject.cpp index 3f12473dca2..d73e8713b87 100644 --- a/ApplicationLibCode/ProjectDataModelCommands/RimcProject.cpp +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcProject.cpp @@ -27,6 +27,7 @@ #include "RicImportSummaryCasesFeature.h" +#include "Cloud/RimCloudDataSourceCollection.h" #include "RimCornerPointCase.h" #include "RimEclipseCaseCollection.h" #include "RimEclipseCellColors.h" @@ -40,6 +41,10 @@ #include "RimTools.h" #include "RimValveTemplateCollection.h" #include "RimWellPathCollection.h" +#include "Sumo/RimSummaryEnsembleSumo.h" +#include "Sumo/RimSummarySumoDataSource.h" + +#include "Summary/RiaSummaryTools.h" #include "RiuMainWindow.h" #include "RiuPlotMainWindow.h" @@ -289,6 +294,67 @@ QString RimProject_createGridFromKeyValues::classKeywordReturnedType() const return RimCornerPointCase::classKeywordStatic(); } +CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimProject, RimProject_importSummaryEnsembleSumo, "importSummaryEnsembleSumo" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimProject_importSummaryEnsembleSumo::RimProject_importSummaryEnsembleSumo( caf::PdmObjectHandle* self ) + : PdmObjectCreationMethod( self ) +{ + CAF_PDM_InitObject( "Import Summary Ensemble from Sumo", "", "", "Import Summary Ensemble from Sumo" ); + + CAF_PDM_InitScriptableFieldNoDefault( &m_caseId, "CaseId", "" ); + CAF_PDM_InitScriptableFieldNoDefault( &m_caseName, "CaseName", "" ); + CAF_PDM_InitScriptableFieldNoDefault( &m_ensembleName, "EnsembleName", "" ); + CAF_PDM_InitScriptableFieldNoDefault( &m_vectorNames, "VectorNames", "" ); + CAF_PDM_InitScriptableFieldNoDefault( &m_realizationIds, "RealizationIds", "" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::expected RimProject_importSummaryEnsembleSumo::execute() +{ + if ( m_caseId().isEmpty() ) return std::unexpected( "Empty case id not allowed" ); + if ( m_ensembleName().isEmpty() ) return std::unexpected( "Empty ensemble name not allowed" ); + + auto cloudDataSourceCollection = RimCloudDataSourceCollection::instance(); + if ( !cloudDataSourceCollection ) return std::unexpected( "No cloud data source collection found." ); + + auto sumCaseMainColl = RiaSummaryTools::summaryCaseMainCollection(); + if ( !sumCaseMainColl ) return std::unexpected( "No summary case main collection found." ); + + auto dataSource = new RimSummarySumoDataSource(); + dataSource->setCaseId( SumoCaseId( m_caseId() ) ); + dataSource->setCaseName( m_caseName() ); + dataSource->setEnsembleName( m_ensembleName() ); + dataSource->setRealizationIds( m_realizationIds() ); + dataSource->setVectorNames( m_vectorNames() ); + dataSource->updateName(); + + cloudDataSourceCollection->addSumoDataSource( dataSource ); + + auto ensemble = new RimSummaryEnsembleSumo(); + ensemble->setUsePathKey1( true ); + ensemble->setSumoDataSource( dataSource ); + sumCaseMainColl->addEnsemble( ensemble ); + ensemble->loadDataAndUpdate(); + + RiaSummaryTools::updateSummaryEnsembleNames(); + sumCaseMainColl->updateAllRequiredEditors(); + + return ensemble; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimProject_importSummaryEnsembleSumo::classKeywordReturnedType() const +{ + return RimSummaryEnsembleSumo::classKeywordStatic(); +} + CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimProject, RimProject_wellPathCollection, "wellPathCollection" ); //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcProject.h b/ApplicationLibCode/ProjectDataModelCommands/RimcProject.h index 1ccdf3240f5..c63fd9144bc 100644 --- a/ApplicationLibCode/ProjectDataModelCommands/RimcProject.h +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcProject.h @@ -100,6 +100,27 @@ class RimProject_createGridFromKeyValues : public caf::PdmObjectCreationMethod caf::PdmField m_actnumKey; }; +//================================================================================================== +/// +//================================================================================================== +class RimProject_importSummaryEnsembleSumo : public caf::PdmObjectCreationMethod +{ + CAF_PDM_HEADER_INIT; + +public: + RimProject_importSummaryEnsembleSumo( caf::PdmObjectHandle* self ); + + std::expected execute() override; + QString classKeywordReturnedType() const override; + +private: + caf::PdmField m_caseId; + caf::PdmField m_caseName; + caf::PdmField m_ensembleName; + caf::PdmField> m_vectorNames; + caf::PdmField> m_realizationIds; +}; + //================================================================================================== /// //================================================================================================== diff --git a/GrpcInterface/Python/rips/PythonExamples/export_and_plotting/sumo_ensemble_import.py b/GrpcInterface/Python/rips/PythonExamples/export_and_plotting/sumo_ensemble_import.py new file mode 100644 index 00000000000..91c2686ab0d --- /dev/null +++ b/GrpcInterface/Python/rips/PythonExamples/export_and_plotting/sumo_ensemble_import.py @@ -0,0 +1,116 @@ +""" +Import a SUMO ensemble into ResInsight using the fmu-sumo Python package. + +This script demonstrates how to use the fmu-sumo package to access SUMO metadata +and then import the ensemble into ResInsight for visualization and analysis. + +The fmu-sumo package provides a high-level API for accessing SUMO data, replacing +the fragile direct Elastic search queries used by ResInsight's built-in SUMO connector. + +Prerequisites: + pip install fmu-sumo rips + +Usage: + python sumo_ensemble_import.py +""" + +# Load ResInsight Processing Server Client Library +import rips + +# Load the fmu-sumo Explorer for metadata access +from fmu.sumo.explorer import Explorer + + +def import_sumo_ensemble( + asset_name: str, + case_name: str, + iteration_name: str, +) -> None: + """Import a SUMO ensemble into ResInsight. + + Uses fmu-sumo to access SUMO metadata (cases, ensembles, vector names, + realization IDs) and then creates the corresponding ensemble in ResInsight. + The actual summary data is fetched lazily by ResInsight when plots are created. + + Arguments: + asset_name (str): The SUMO asset (field) name, e.g. "Drogon" + case_name (str): The SUMO case name to search for + iteration_name (str): The ensemble/iteration name within the case + """ + + # Connect to ResInsight instance + resinsight = rips.Instance.find() + project = resinsight.project + + # Connect to SUMO using fmu-sumo Explorer + # The Explorer handles authentication automatically + sumo = Explorer(env="prod") + + # Find cases matching the given asset and case name + cases = sumo.cases.filter(asset=asset_name) + matching_cases = [c for c in cases if c.name == case_name] + + if not matching_cases: + print(f"No case found with name '{case_name}' for asset '{asset_name}'") + return + + sumo_case = matching_cases[0] + print(f"Found SUMO case: {sumo_case.name} (id: {sumo_case.uuid})") + + # Find the ensemble/iteration within the case + iterations = sumo_case.iterations + matching_iterations = [it for it in iterations if it.name == iteration_name] + + if not matching_iterations: + available = [it.name for it in iterations] + print(f"Iteration '{iteration_name}' not found. Available: {available}") + return + + iteration = matching_iterations[0] + print(f"Found iteration: {iteration.name}") + + # Get realization IDs from the iteration + realization_ids = [str(r) for r in iteration.realizations] + print(f"Found {len(realization_ids)} realizations") + + # Find summary tables within the iteration + summary_tables = iteration.tables.filter(tagname="summary") + + if not summary_tables: + print("No summary tables found for this iteration") + return + + summary_table = summary_tables[0] + + # Get vector names (column names) from the summary table + # Exclude metadata columns (DATE, REAL, ENSEMBLE, etc.) + metadata_columns = {"DATE", "REAL", "ENSEMBLE", "ITER"} + vector_names = [ + col for col in summary_table.column_names if col.upper() not in metadata_columns + ] + print(f"Found {len(vector_names)} summary vectors") + + # Import the ensemble into ResInsight + # This creates a SummaryEnsembleSumo object that will lazily load + # the actual parquet data when plots are created + ensemble = project.import_summary_ensemble_sumo( + case_id=sumo_case.uuid, + case_name=sumo_case.name, + ensemble_name=iteration.name, + vector_names=vector_names, + realization_ids=realization_ids, + ) + + if ensemble is not None: + print(f"Successfully imported SUMO ensemble: {ensemble.name}") + else: + print("Failed to import SUMO ensemble") + + +if __name__ == "__main__": + # Example: import a Drogon ensemble from SUMO + import_sumo_ensemble( + asset_name="Drogon", + case_name="Drogon_AHM_2023-02-01", + iteration_name="iter-0", + ) From 2c9c3f3ec7a53bdec5e8ad94be5611f7cd4450c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 20:23:24 +0000 Subject: [PATCH 3/3] #issue Address code review feedback: fix capitalization and add null check Agent-Logs-Url: https://github.com/magnesj/ResInsight/sessions/3a967829-5337-492b-b130-d83837d37ea9 Co-authored-by: magnesj <1793152+magnesj@users.noreply.github.com> --- .../ProjectDataModel/Cloud/RimCloudDataSourceCollection.cpp | 1 + ApplicationLibCode/ProjectDataModelCommands/RimcProject.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ApplicationLibCode/ProjectDataModel/Cloud/RimCloudDataSourceCollection.cpp b/ApplicationLibCode/ProjectDataModel/Cloud/RimCloudDataSourceCollection.cpp index 2bfc8afbd30..dc9eda8571d 100644 --- a/ApplicationLibCode/ProjectDataModel/Cloud/RimCloudDataSourceCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/Cloud/RimCloudDataSourceCollection.cpp @@ -106,6 +106,7 @@ void RimCloudDataSourceCollection::createEnsemblesFromSelectedDataSources( const //-------------------------------------------------------------------------------------------------- void RimCloudDataSourceCollection::addSumoDataSource( RimSummarySumoDataSource* dataSource ) { + if ( !dataSource ) return; m_sumoDataSources.push_back( dataSource ); } diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcProject.cpp b/ApplicationLibCode/ProjectDataModelCommands/RimcProject.cpp index d73e8713b87..3a527a56f2b 100644 --- a/ApplicationLibCode/ProjectDataModelCommands/RimcProject.cpp +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcProject.cpp @@ -316,7 +316,7 @@ RimProject_importSummaryEnsembleSumo::RimProject_importSummaryEnsembleSumo( caf: //-------------------------------------------------------------------------------------------------- std::expected RimProject_importSummaryEnsembleSumo::execute() { - if ( m_caseId().isEmpty() ) return std::unexpected( "Empty case id not allowed" ); + if ( m_caseId().isEmpty() ) return std::unexpected( "Empty case ID not allowed" ); if ( m_ensembleName().isEmpty() ) return std::unexpected( "Empty ensemble name not allowed" ); auto cloudDataSourceCollection = RimCloudDataSourceCollection::instance();