Sector model export: support WLIST kw (#13641)

* Support ADD and NEW operations in WLIST kw
* Add support for DEL operation
This commit is contained in:
JJ authored and GitHub committed 2026-02-23 20:15:04 +01:00
1 parent ab1cd9bb22
commit ecd89bc8ab
6 files changed
+183 -9

No files matched your search

@@ -135,4 +135,26 @@ Opm::DeckItem optionalItem( std::string name, std::optional<float> value )
return defaultItem( name );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
Opm::DeckItem item( std::string name, std::set<std::string> values )
{
Opm::DeckItem item1( name, "" );
for ( const auto& value : values )
item1.push_back( value );
return item1;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
Opm::DeckItem item( std::string name, std::vector<std::string> values )
{
Opm::DeckItem item1( name, "" );
for ( const auto& value : values )
item1.push_back( value );
return item1;
}
} // namespace RifOpmDeckTools
@@ -19,7 +19,9 @@
#pragma once
#include <optional>
#include <set>
#include <string>
#include <vector>
namespace Opm
{
@@ -38,5 +40,7 @@ Opm::DeckItem item( std::string name, double value );
Opm::DeckItem optionalItem( std::string name, std::optional<float> value );
Opm::DeckItem optionalItem( std::string name, std::optional<double> value );
Opm::DeckItem defaultItem( std::string name );
Opm::DeckItem item( std::string name, std::set<std::string> values );
Opm::DeckItem item( std::string name, std::vector<std::string> values );
} // namespace RifOpmDeckTools
@@ -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
//--------------------------------------------------------------------------------------------------
@@ -104,7 +104,8 @@ public:
bool replaceAllKeywords( const std::string& keywordName, const std::vector<Opm::DeckKeyword>& 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();
@@ -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<void, QString> 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<Opm::DeckRecord, QString> RigSimulationInputTool::processBoxRecord
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<void, QString> RigSimulationInputTool::filterAndUpdateWellKeywords( RimEclipseCase* eclipseCase,
const RigSimulationInputSettings& settings,
RifOpmFlowDeckFile& deckFile )
std::set<std::string> 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<void, QString> RigSimulationInputTool::filterAndUpdateWellKeywords
return names;
}() ) ) );
return validWellNames;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<void, QString> RigSimulationInputTool::updateWellListKeywords( std::set<std::string>& 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<std::string> existingLists;
// for each WLIST keyword
for ( auto [index, kw] : keywordsWithIndices )
{
std::map<std::string, std::set<std::string>> wellLists;
std::map<std::string, std::set<std::string>> 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<std::string>( 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<std::string>( 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<std::string>( 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<Opm::DeckItem> 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<Opm::DeckItem> 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<void, QString> RigSimulationInputTool::filterAndUpdateWellKeywords( std::set<std::string>& validWellNames,
const RigSimulationInputSettings& settings,
RifOpmFlowDeckFile& deckFile )
{
// List of well-related keywords to filter (keywords that reference well names)
std::vector<std::string> wellKeywords = { "COMPDAT", "COMPLUMP", "COMPORD", "COMPSEGS", "WCONHIST", "WCONINJE", "WCONINJH",
"WCONPROD", "WCYCLE", "WDFAC", "WDFACCOR", "WEFAC", "WELCNTL", "WELOPEN",
@@ -1361,7 +1487,7 @@ std::expected<void, QString> 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<void, QString> 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
@@ -26,6 +26,7 @@
#include <expected>
#include <functional>
#include <set>
#include <string>
#include <vector>
@@ -148,8 +149,15 @@ private:
static std::expected<void, QString>
addFaultsToDeckFile( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );
static std::expected<void, QString>
filterAndUpdateWellKeywords( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );
static std::expected<void, QString> filterAndUpdateWellKeywords( std::set<std::string>& validWellNames,
const RigSimulationInputSettings& settings,
RifOpmFlowDeckFile& deckFile );
static std::expected<void, QString> updateWellListKeywords( std::set<std::string>& validWellNames,
const RigSimulationInputSettings& settings,
RifOpmFlowDeckFile& deckFile );
static std::set<std::string> wellNamesToInclude( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings );
static std::expected<void, QString>
exportEditNncKeyword( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );