diff --git a/ApplicationLibCode/FileInterface/RifOpmDeckTools.cpp b/ApplicationLibCode/FileInterface/RifOpmDeckTools.cpp index 5cbd77a6e7..6600f17455 100644 --- a/ApplicationLibCode/FileInterface/RifOpmDeckTools.cpp +++ b/ApplicationLibCode/FileInterface/RifOpmDeckTools.cpp @@ -135,4 +135,26 @@ Opm::DeckItem optionalItem( std::string name, std::optional value ) return defaultItem( name ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +Opm::DeckItem item( std::string name, std::set values ) +{ + Opm::DeckItem item1( name, "" ); + for ( const auto& value : values ) + item1.push_back( value ); + return item1; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +Opm::DeckItem item( std::string name, std::vector values ) +{ + Opm::DeckItem item1( name, "" ); + for ( const auto& value : values ) + item1.push_back( value ); + return item1; +} + } // namespace RifOpmDeckTools diff --git a/ApplicationLibCode/FileInterface/RifOpmDeckTools.h b/ApplicationLibCode/FileInterface/RifOpmDeckTools.h index 30dd9b2c00..f27cd6c785 100644 --- a/ApplicationLibCode/FileInterface/RifOpmDeckTools.h +++ b/ApplicationLibCode/FileInterface/RifOpmDeckTools.h @@ -19,7 +19,9 @@ #pragma once #include +#include #include +#include namespace Opm { @@ -38,5 +40,7 @@ Opm::DeckItem item( std::string name, double value ); Opm::DeckItem optionalItem( std::string name, std::optional value ); Opm::DeckItem optionalItem( std::string name, std::optional value ); Opm::DeckItem defaultItem( std::string name ); +Opm::DeckItem item( std::string name, std::set values ); +Opm::DeckItem item( std::string name, std::vector values ); } // namespace RifOpmDeckTools diff --git a/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.cpp b/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.cpp index aa739c059f..dd58137bb2 100644 --- a/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.cpp +++ b/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.cpp @@ -986,6 +986,19 @@ bool RifOpmFlowDeckFile::replaceKeywordAtIndex( const Opm::FileDeck::Index& inde return true; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifOpmFlowDeckFile::removeKeywordAtIndex( const Opm::FileDeck::Index& index ) +{ + if ( m_fileDeck.get() == nullptr ) return false; + + // Erase the keyword at the given index and insert the new one + m_fileDeck->erase( index ); + + return true; +} + //-------------------------------------------------------------------------------------------------- /// Returns number of removed keywords //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.h b/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.h index e6083e652c..1dbcf36310 100644 --- a/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.h +++ b/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.h @@ -104,7 +104,8 @@ class RifOpmFlowDeckFile bool replaceAllKeywords( const std::string& keywordName, const std::vector& keywords ); bool replaceKeywordAtIndex( const Opm::FileDeck::Index& index, const Opm::DeckKeyword& keyword ); - int removeKeywords( const std::string& keywordName ); + int removeKeywords( const std::string& keywordName ); + bool removeKeywordAtIndex( const Opm::FileDeck::Index& index ); private: void splitDatesIfNecessary(); diff --git a/ApplicationLibCode/ReservoirDataModel/SimulationFile/RigSimulationInputTool.cpp b/ApplicationLibCode/ReservoirDataModel/SimulationFile/RigSimulationInputTool.cpp index 2f82361529..45fc9863b4 100644 --- a/ApplicationLibCode/ReservoirDataModel/SimulationFile/RigSimulationInputTool.cpp +++ b/ApplicationLibCode/ReservoirDataModel/SimulationFile/RigSimulationInputTool.cpp @@ -20,6 +20,7 @@ #include "RiaLogging.h" #include "RiaNncDefines.h" +#include "RiaStdStringTools.h" #include "RigModelPaddingSettings.h" #include "RigPadModel.h" @@ -145,7 +146,14 @@ std::expected RigSimulationInputTool::exportSimulationInput( RimE return result; } - if ( auto result = filterAndUpdateWellKeywords( &eclipseCase, settings, deckFile ); !result ) + auto validWellNames = wellNamesToInclude( &eclipseCase, settings ); + + if ( auto result = filterAndUpdateWellKeywords( validWellNames, settings, deckFile ); !result ) + { + return result; + } + + if ( auto result = updateWellListKeywords( validWellNames, settings, deckFile ); !result ) { return result; } @@ -1269,9 +1277,7 @@ std::expected RigSimulationInputTool::processBoxRecord //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -std::expected RigSimulationInputTool::filterAndUpdateWellKeywords( RimEclipseCase* eclipseCase, - const RigSimulationInputSettings& settings, - RifOpmFlowDeckFile& deckFile ) +std::set RigSimulationInputTool::wellNamesToInclude( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings ) { // Find wells that intersect with the sector auto intersectingWells = findIntersectingWells( eclipseCase, settings.min(), settings.max() ); @@ -1306,6 +1312,126 @@ std::expected RigSimulationInputTool::filterAndUpdateWellKeywords return names; }() ) ) ); + return validWellNames; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::expected RigSimulationInputTool::updateWellListKeywords( std::set& validWellNames, + const RigSimulationInputSettings& settings, + RifOpmFlowDeckFile& deckFile ) +{ + using W = Opm::ParserKeywords::WLIST; + + int replacedCount = 0; + int removedCount = 0; + + // gather existing lists + + auto keywordsWithIndices = deckFile.findAllKeywordsWithIndices( W::keywordName ); + if ( keywordsWithIndices.empty() ) return {}; + + std::set existingLists; + + // for each WLIST keyword + for ( auto [index, kw] : keywordsWithIndices ) + { + std::map> wellLists; + std::map> deleteLists; + + // for each list operation in this keyword + for ( size_t recordIdx = 0; recordIdx < kw.size(); recordIdx++ ) + { + const auto& record = kw.getRecord( recordIdx ); + if ( record.size() < 3 ) continue; + + // the list name + const auto& listNameItem = record.getItem( 0 ); + if ( !listNameItem.hasValue( 0 ) || listNameItem.getType() != Opm::type_tag::string ) continue; + std::string listName = listNameItem.get( 0 ); + + // the list operation + const auto& operationItem = record.getItem( 1 ); + if ( !operationItem.hasValue( 0 ) || operationItem.getType() != Opm::type_tag::string ) continue; + std::string operationName = operationItem.get( 0 ); + operationName = RiaStdStringTools::toUpper( operationName ); + + bool delOperation = operationName == "DEL"; + if ( operationName != "ADD" && operationName != "NEW" && !delOperation ) + { + RiaLogging::warning( QString( "Unsupported %1 operation '%2' in list '%3', skipping" ) + .arg( W::keywordName.c_str() ) + .arg( operationName.c_str() ) + .arg( listName.c_str() ) ); + continue; + } + + // the list of wells to do something with, only include the ones in our valid list + const auto& wellsItem = record.getItem( 2 ); + for ( size_t i = 0; i < wellsItem.data_size(); i++ ) + { + std::string wellName = wellsItem.get( i ); + if ( validWellNames.contains( wellName ) ) + { + if ( delOperation ) + deleteLists[listName].insert( wellName ); + else + wellLists[listName].insert( wellName ); + } + } + } + Opm::DeckKeyword newKw( kw.location(), kw.name() ); + + for ( const auto& [listName, wells] : wellLists ) + { + if ( wells.empty() ) continue; + std::vector items; + items.push_back( RifOpmDeckTools::item( "NAME", listName ) ); + const std::string action = existingLists.contains( listName ) ? "ADD" : "NEW"; + items.push_back( RifOpmDeckTools::item( "ACTION", action ) ); + items.push_back( RifOpmDeckTools::item( "WELLS", wells ) ); + newKw.addRecord( Opm::DeckRecord{ std::move( items ) } ); + existingLists.insert( listName ); + } + + for ( const auto& [listName, wells] : deleteLists ) + { + if ( wells.empty() ) continue; + std::vector items; + items.push_back( RifOpmDeckTools::item( "NAME", listName ) ); + items.push_back( RifOpmDeckTools::item( "ACTION", "DEL" ) ); + items.push_back( RifOpmDeckTools::item( "WELLS", wells ) ); + newKw.addRecord( Opm::DeckRecord{ std::move( items ) } ); + } + + if ( newKw.size() > 0 ) + { + // replace the first wlist kw with the new one, remove remaining kws + deckFile.replaceKeywordAtIndex( index, std::move( newKw ) ); + replacedCount++; + } + else + { + // replace with SKIP kw to remove later + deckFile.replaceKeywordAtIndex( index, Opm::DeckKeyword( kw.location(), "SKIP" ) ); + removedCount++; + } + } + + RiaLogging::info( + QString( "Processed keyword '%1': %2 updated, %3 removed" ).arg( W::keywordName.c_str() ).arg( replacedCount ).arg( removedCount ) ); + + return {}; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::expected RigSimulationInputTool::filterAndUpdateWellKeywords( std::set& validWellNames, + const RigSimulationInputSettings& settings, + RifOpmFlowDeckFile& deckFile ) +{ // List of well-related keywords to filter (keywords that reference well names) std::vector wellKeywords = { "COMPDAT", "COMPLUMP", "COMPORD", "COMPSEGS", "WCONHIST", "WCONINJE", "WCONINJH", "WCONPROD", "WCYCLE", "WDFAC", "WDFACCOR", "WEFAC", "WELCNTL", "WELOPEN", @@ -1361,7 +1487,7 @@ std::expected RigSimulationInputTool::filterAndUpdateWellKeywords if ( keywordName == "COMPSEGS" || keywordName == "WELSEGS" ) { currentSegmentWell = wellName; - keepSegmentRecords = ( validWellNames.find( wellName ) != validWellNames.end() ); + keepSegmentRecords = ( validWellNames.contains( wellName ) ); } } else if ( keywordName == "COMPSEGS" || keywordName == "WELSEGS" ) @@ -1371,7 +1497,7 @@ std::expected RigSimulationInputTool::filterAndUpdateWellKeywords } // Check if this well is in our valid set - if ( ( isWellNameRecord && validWellNames.find( wellName ) != validWellNames.end() ) || + if ( ( isWellNameRecord && validWellNames.contains( wellName ) ) || ( !isWellNameRecord && ( keywordName == "COMPSEGS" || keywordName == "WELSEGS" ) && keepSegmentRecords ) ) { // For keywords with IJK coordinates, we need to transform them diff --git a/ApplicationLibCode/ReservoirDataModel/SimulationFile/RigSimulationInputTool.h b/ApplicationLibCode/ReservoirDataModel/SimulationFile/RigSimulationInputTool.h index 46f19299ad..3e8c7c4867 100644 --- a/ApplicationLibCode/ReservoirDataModel/SimulationFile/RigSimulationInputTool.h +++ b/ApplicationLibCode/ReservoirDataModel/SimulationFile/RigSimulationInputTool.h @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -148,8 +149,15 @@ class RigSimulationInputTool static std::expected addFaultsToDeckFile( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile ); - static std::expected - filterAndUpdateWellKeywords( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile ); + static std::expected filterAndUpdateWellKeywords( std::set& validWellNames, + const RigSimulationInputSettings& settings, + RifOpmFlowDeckFile& deckFile ); + + static std::expected updateWellListKeywords( std::set& validWellNames, + const RigSimulationInputSettings& settings, + RifOpmFlowDeckFile& deckFile ); + + static std::set wellNamesToInclude( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings ); static std::expected exportEditNncKeyword( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );