From 1336eba81d5153c9faa95836312d33340beb5b83 Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Fri, 28 Aug 2026 18:25:49 +0200 Subject: [PATCH] #14635 Orion Events: Use well name alias for generic keywords --- .../RicScheduleDataGenerator.cpp | 3 +- .../RifEventKeywordFormatter.cpp | 13 ++++++--- .../FileInterface/RifEventKeywordFormatter.h | 4 ++- .../UnitTests/RifOpmFlowDeckFile-Test.cpp | 18 ++++++++++++ .../Python/rips/tests/test_orion_events.py | 28 +++++++++++++++++++ 5 files changed, 60 insertions(+), 6 deletions(-) diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicScheduleDataGenerator.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/RicScheduleDataGenerator.cpp index f69d5fcc4d..8932aa02b9 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/RicScheduleDataGenerator.cpp +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicScheduleDataGenerator.cpp @@ -602,7 +602,8 @@ void RicScheduleDataGenerator::generateWellControlForWell( const RimWellEventTim { if ( event->wellName() != well.name() ) continue; - auto kw = RifEventKeywordFormatter::buildWellEvent( event, well.name() ); + const QString wellName = well.completionSettings() ? well.completionSettings()->wellNameForExport() : well.name(); + auto kw = RifEventKeywordFormatter::buildWellEvent( event, wellName ); if ( !kw ) continue; const QString name = QString::fromStdString( kw->name() ); diff --git a/ApplicationLibCode/FileInterface/RifEventKeywordFormatter.cpp b/ApplicationLibCode/FileInterface/RifEventKeywordFormatter.cpp index 7cac27cf57..62b8041728 100644 --- a/ApplicationLibCode/FileInterface/RifEventKeywordFormatter.cpp +++ b/ApplicationLibCode/FileInterface/RifEventKeywordFormatter.cpp @@ -77,7 +77,8 @@ bool RifEventKeywordFormatter::isRecordlessKeyword( const Opm::DeckKeyword& keyw /// //-------------------------------------------------------------------------------------------------- std::optional RifEventKeywordFormatter::buildKeyword( const QString& keywordName, - const std::vector& items ) + const std::vector& items, + const std::optional& wellNameOverride ) { QString keyword = keywordName.toUpper(); std::string kwName = keyword.toStdString(); @@ -187,7 +188,8 @@ std::optional RifEventKeywordFormatter::buildKeyword( const QS std::optional lastProvidedIdx; for ( size_t i = 0; i < record.size(); ++i ) { - if ( userItemsByName.contains( record.get( i ).name() ) ) lastProvidedIdx = i; + const std::string& name = record.get( i ).name(); + if ( userItemsByName.contains( name ) || ( name == "WELL" && wellNameOverride.has_value() ) ) lastProvidedIdx = i; } std::vector deckItems; @@ -198,7 +200,9 @@ std::optional RifEventKeywordFormatter::buildKeyword( const QS { const std::string& name = record.get( i ).name(); auto it = userItemsByName.find( name ); - if ( it == userItemsByName.end() ) + if ( name == "WELL" && wellNameOverride.has_value() ) + deckItems.push_back( RifOpmDeckTools::item( name, wellNameOverride->toStdString() ) ); + else if ( it == userItemsByName.end() ) deckItems.push_back( RifOpmDeckTools::defaultItem( name ) ); else appendDeckItem( deckItems, name, it->second ); @@ -361,7 +365,8 @@ std::optional RifEventKeywordFormatter::buildWellEvent( const const auto* keywordEvent = dynamic_cast( event ); if ( keywordEvent ) { - return buildKeyword( keywordEvent->keywordName(), keywordEvent->items() ); + const std::optional wellNameOverride = wellName.isEmpty() ? std::nullopt : std::optional( wellName ); + return buildKeyword( keywordEvent->keywordName(), keywordEvent->items(), wellNameOverride ); } } diff --git a/ApplicationLibCode/FileInterface/RifEventKeywordFormatter.h b/ApplicationLibCode/FileInterface/RifEventKeywordFormatter.h index 20e21968ef..f6c5150f21 100644 --- a/ApplicationLibCode/FileInterface/RifEventKeywordFormatter.h +++ b/ApplicationLibCode/FileInterface/RifEventKeywordFormatter.h @@ -36,7 +36,9 @@ namespace RifEventKeywordFormatter bool isRecordlessKeyword( const Opm::DeckKeyword& keyword ); // build*: produce a structured Opm::DeckKeyword (used by callers that merge records across wells). -std::optional buildKeyword( const QString& keywordName, const std::vector& items ); +std::optional buildKeyword( const QString& keywordName, + const std::vector& items, + const std::optional& wellNameOverride = std::nullopt ); std::optional buildWconprod( const RimWellEventControl* controlEvent, const QString& wellName ); std::optional buildWconinje( const RimWellEventControl* controlEvent, const QString& wellName ); std::optional buildWellEvent( const RimWellEvent* event, const QString& wellName ); diff --git a/ApplicationLibCode/UnitTests/RifOpmFlowDeckFile-Test.cpp b/ApplicationLibCode/UnitTests/RifOpmFlowDeckFile-Test.cpp index 90216b2bae..57f5f073f7 100644 --- a/ApplicationLibCode/UnitTests/RifOpmFlowDeckFile-Test.cpp +++ b/ApplicationLibCode/UnitTests/RifOpmFlowDeckFile-Test.cpp @@ -2,12 +2,14 @@ #include "RiaTestDataDirectory.h" +#include "RifEventKeywordFormatter.h" #include "RifOpmDeckTools.h" #include "RifOpmFlowDeckFile.h" #include "RigEclipseResultTools.h" #include "ProjectDataModel/Jobs/RimKeywordFactory.h" +#include "ProjectDataModel/WellEvents/RimWellEventKeywordItem.h" #include "cvfStructGrid.h" @@ -755,6 +757,22 @@ TEST( RifOpmFlowDeckFileTest, SaveDeckPreservesIncludeOnlyWrapperFiles ) EXPECT_EQ( 2, std::count( keywords.begin(), keywords.end(), std::string( "VFPPROD" ) ) ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RifEventKeywordFormatterTest, BuildKeywordOverridesWellName ) +{ + RimWellEventKeywordItem wellItem; + wellItem.setItemName( "WELL" ); + wellItem.setStringValue( "WellPathA" ); + + auto keyword = RifEventKeywordFormatter::buildKeyword( "WCONHIST", { &wellItem }, QString( "ORION_EXPORT_ALIAS" ) ); + + ASSERT_TRUE( keyword.has_value() ); + ASSERT_EQ( keyword->size(), 1 ); + EXPECT_EQ( keyword->getRecord( 0 ).getItem( "WELL" ).getTrimmedString( 0 ), "ORION_EXPORT_ALIAS" ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/GrpcInterface/Python/rips/tests/test_orion_events.py b/GrpcInterface/Python/rips/tests/test_orion_events.py index e78a4bacf5..b36bac5ba3 100644 --- a/GrpcInterface/Python/rips/tests/test_orion_events.py +++ b/GrpcInterface/Python/rips/tests/test_orion_events.py @@ -1910,6 +1910,34 @@ class TestOrionEventsIntegration: assert f'Line 3 [WELL "{well.name}", date 2024-01-01]' in error_msg assert "Keyword 'NOT_A_KEYWORD' is not recognized by opm-common" in error_msg + def test_generic_well_keywords_use_export_alias(self, project_with_case_and_wells): + project, case, timeline = project_with_case_and_wells + well = project.well_paths()[0] + + export_alias = "ORION_EXPORT_ALIAS" + settings = well.completion_settings() + settings.well_name_for_export = export_alias + settings.update() + + document = parse_orion_events( + "ORIONEVENTS 2.0\n" + f'WELL "{well.name}"\n' + " 2024-01-01 WCONHIST STATUS=OPEN CMODE=ORAT ORAT=100\n" + " 2024-01-01 WELTARG CMODE=ORAT VALUE=200\n" + " 2024-01-01 WRFTPLT OUTPUT_RFT=YES OUTPUT_PLT=NO OUTPUT_SEGMENT=NO\n" + ) + report = apply_orion_document(document, timeline, project) + assert report.errors == [] + + schedule = timeline.generate_schedule_text( + eclipse_case=case, + first_date_as_comment=False, + ) + for keyword in ("WCONHIST", "WELTARG", "WRFTPLT"): + block = schedule.split(keyword, 1)[1].split("/", 1)[0] + assert f"'{export_alias}'" in block + assert f"'{well.name}'" not in block + def test_rptrst_boolean_values_emit_bare_mnemonics( self, project_with_case_and_wells ):