From 6a15c844634c3a1ea129a41422889e5d01ff527b Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Tue, 20 Oct 2026 13:13:28 +0200 Subject: [PATCH] #13485: Export unique top-level well paths for completions and MSW --- ...ellPathExportCompletionDataFeatureImpl.cpp | 5 +- .../RicWellPathExportCompletionsFileTools.cpp | 19 ++++++ .../RicWellPathExportCompletionsFileTools.h | 4 ++ .../RicWellPathExportMswCompletionsImpl.cpp | 5 +- .../rips/tests/test_msw_completion_data.py | 59 +++++++++++++++++++ 5 files changed, 90 insertions(+), 2 deletions(-) diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.cpp index f1a26010cb..346d05e782 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.cpp +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.cpp @@ -75,9 +75,12 @@ //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RicWellPathExportCompletionDataFeatureImpl::exportCompletions( const std::vector& wellPaths, +void RicWellPathExportCompletionDataFeatureImpl::exportCompletions( const std::vector& allWellPaths, const RicExportCompletionDataSettingsUi& exportSettings ) { + // Laterals are exported together with their top level well path + const auto wellPaths = RicWellPathExportCompletionsFileTools::uniqueTopLevelWellPaths( allWellPaths ); + if ( exportSettings.caseToApply() == nullptr ) { RiaLogging::error( "Export Completions Data: Cannot export completions data a valid Eclipse case" ); diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionsFileTools.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionsFileTools.cpp index dd6b435342..12b1b62407 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionsFileTools.cpp +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionsFileTools.cpp @@ -29,6 +29,8 @@ #include "cafUtils.h" +#include + #include #include #include @@ -66,6 +68,23 @@ std::shared_ptr RicWellPathExportCompletionsFileTools::openFile( const QS return exportFile; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RicWellPathExportCompletionsFileTools::uniqueTopLevelWellPaths( const std::vector& wellPaths ) +{ + std::vector result; + for ( auto* wellPath : wellPaths ) + { + if ( !wellPath ) continue; + + auto* topLevel = wellPath->topLevelWellPath(); + if ( std::find( result.begin(), result.end(), topLevel ) == result.end() ) result.push_back( topLevel ); + } + + return result; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionsFileTools.h b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionsFileTools.h index 04672fe848..c1abf1371d 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionsFileTools.h +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportCompletionsFileTools.h @@ -22,12 +22,16 @@ #include #include +#include class RimWellPath; class RicWellPathExportCompletionsFileTools { public: + // Replaces laterals by their top level well path and removes duplicates, keeping the original order + static std::vector uniqueTopLevelWellPaths( const std::vector& wellPaths ); + static const RimWellPath* findWellPathFromExportName( const QString& wellNameForExport ); static std::shared_ptr diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportMswCompletionsImpl.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportMswCompletionsImpl.cpp index fd9bac7379..527f18e371 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportMswCompletionsImpl.cpp +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportMswCompletionsImpl.cpp @@ -198,8 +198,11 @@ void exportSplitMswData( const RicExportCompletionDataSettingsUi& exportSettings /// //-------------------------------------------------------------------------------------------------- void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForAllCompletions( const RicExportCompletionDataSettingsUi& exportSettings, - const std::vector& wellPaths ) + const std::vector& allWellPaths ) { + // Laterals are exported together with their top level well path + const auto wellPaths = RicWellPathExportCompletionsFileTools::uniqueTopLevelWellPaths( allWellPaths ); + if ( exportSettings.fileSplit() == RicExportCompletionDataSettingsUi::ExportSplit::SPLIT_ON_WELL ) { internal::exportSplitMswData( exportSettings, exportSettings.folder(), wellPaths ); diff --git a/GrpcInterface/Python/rips/tests/test_msw_completion_data.py b/GrpcInterface/Python/rips/tests/test_msw_completion_data.py index 69863446d6..2ccd2059d0 100644 --- a/GrpcInterface/Python/rips/tests/test_msw_completion_data.py +++ b/GrpcInterface/Python/rips/tests/test_msw_completion_data.py @@ -262,3 +262,62 @@ def test_msw_settings_read(rips_instance, initialize_test): assert completion_settings.allow_well_cross_flow, ( "Allow well cross flow should be True" ) + + +def _export_msw_files(case, well_paths, export_function): + with tempfile.TemporaryDirectory(prefix="rips") as folder: + export_function(case, well_paths, folder) + files = {} + for name in os.listdir(folder): + if name.endswith("_MSW"): + with open(os.path.join(folder, name)) as f: + # Drop comment lines, they contain a timestamp + files[name] = [ + line + for line in f.read().splitlines() + if not line.startswith("--") + ] + return files + + +def _export_completions(case, well_paths, folder): + case.export_completions( + well_paths=well_paths, + time_step=0, + export_folder=folder, + file_split=rips.CompletionExportSplit.SPLIT_ON_WELL, + include_msw=True, + export_comments=False, + ) + + +def _export_msw_completions(case, well_paths, folder): + case.export_msw_completions( + well_paths=well_paths, + export_folder=folder, + file_split=rips.CompletionExportSplit.SPLIT_ON_WELL, + ) + + +@pytest.mark.parametrize( + "export_function", [_export_completions, _export_msw_completions] +) +def test_msw_export_main_and_lateral_exports_top_level_well( + rips_instance, initialize_test, export_function +): + """Issue #13485: passing both a main well and its lateral must export the + main well (including all laterals), not only the lateral.""" + project_path = dataroot.PATH + "/msw-export/project-files/perf_lateral.rsp" + project = rips_instance.project.open(path=project_path) + case = project.cases()[0] + + well_y1 = project.well_path_by_name("Well-1 Y1") + well_y2 = project.well_path_by_name("Well-1 Y2") + assert well_y1 is not None and well_y2 is not None + + reference = _export_msw_files(case, [well_y1], export_function) + assert len(reference) == 1 + + for well_paths in ([well_y1, well_y2], [well_y2, well_y1], [well_y2]): + exported = _export_msw_files(case, well_paths, export_function) + assert exported == reference