Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

#include "opm/input/eclipse/Deck/DeckKeyword.hpp"

#include <map>
#include <set>

//--------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -95,53 +96,51 @@ QString RicScheduleDataGenerator::generateDateSection( const RimWellEventTimelin
const std::vector<RimWellPath*>& wellPaths,
const QDateTime& date )
{
// Keyword priority order for output
static const std::vector<QString> keywordOrder = { "WELSPECS",
"COMPORD",
"GRUPTREE",
"COMPDAT",
"COMPLUMP",
"WELSEGS",
"COMPSEGS",
"WCONHIST",
"WCONINJH",
"WCONPROD",
"WCONINJE",
"WRFTPLT",
"TUNING",
"RPTSCHED",
"RPTRST",
"WSEGVALV",
"WSEGAICD" };

QString result;

// Generate DATES keyword
result += RimKeywordFactory::deckKeywordToString( RimKeywordFactory::datesKeyword( date ) ) + "\n";

// Collect all output for this date
QString compdatData;
QString mswData;
QString wellControlData;
// Collect all keyword output into a map keyed by keyword name
std::map<QString, QString> keywordBlocks;

for ( auto* well : wellPaths )
{
if ( !well ) continue;

QString wellCompdat = generateCompdatForWell( timeline, eclipseCase, *well, date );
if ( !wellCompdat.isEmpty() )
QString welspecsKw = generateWelspecsForWell( timeline, eclipseCase, *well, date );
if ( !welspecsKw.isEmpty() )
{
compdatData += wellCompdat;
keywordBlocks["WELSPECS"] += welspecsKw;
}

QString wellMsw = generateMswForWell( timeline, eclipseCase, *well, date );
if ( !wellMsw.isEmpty() )
QString wellCompdat = generateCompdatForWell( timeline, eclipseCase, *well, date );
if ( !wellCompdat.isEmpty() )
{
mswData += wellMsw;
keywordBlocks["COMPDAT"] += wellCompdat;
}

QString wellControl = generateWellControlForWell( timeline, *well, date );
if ( !wellControl.isEmpty() )
{
wellControlData += wellControl;
}
}

// Output collected data
if ( !compdatData.isEmpty() )
{
result += compdatData;
}

if ( !mswData.isEmpty() )
{
result += mswData;
}

if ( !wellControlData.isEmpty() )
{
result += wellControlData;
generateMswForWell( timeline, eclipseCase, *well, date, keywordBlocks );
generateWellControlForWell( timeline, *well, date, keywordBlocks );
}

// Process schedule-level keyword events (not tied to a specific well)
Expand All @@ -156,13 +155,68 @@ QString RicScheduleDataGenerator::generateDateSection( const RimWellEventTimelin
QString keywordStr = keywordEvent->generateScheduleKeyword( "" );
if ( !keywordStr.isEmpty() )
{
result += keywordStr;
result += "\n";
keywordBlocks[keywordEvent->keywordName().toUpper()] += keywordStr + "\n";
}
}
}
}

// Output keywords in priority order
std::set<QString> emitted;
for ( const auto& kw : keywordOrder )
{
auto it = keywordBlocks.find( kw );
if ( it != keywordBlocks.end() && !it->second.isEmpty() )
{
result += it->second;
emitted.insert( kw );
}
}

// Output remaining keywords not in priority list
for ( const auto& [kw, data] : keywordBlocks )
{
if ( emitted.find( kw ) == emitted.end() && !data.isEmpty() )
{
result += data;
}
}

return result;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicScheduleDataGenerator::generateWelspecsForWell( const RimWellEventTimeline& timeline,
RimEclipseCase& eclipseCase,
RimWellPath& well,
const QDateTime& date )
{
// Get perforation events at this exact date for this well
auto events = timeline.getEventsAtDate( date );

bool hasEvents = false;
for ( auto* event : events )
{
if ( ( event->eventType() == RimWellEvent::EventType::PERF || event->eventType() == RimWellEvent::EventType::VALVE ||
event->eventType() == RimWellEvent::EventType::TUBING ) &&
event->wellName() == well.name() )
{
hasEvents = true;
break;
}
}

if ( !hasEvents ) return QString();

std::string wellGroupName = well.completionSettings()->groupNameForExport().toStdString();
auto welspecsKw = RimKeywordFactory::welspecsKeyword( wellGroupName, &eclipseCase, &well );

QString result;
result += RimKeywordFactory::deckKeywordToString( welspecsKw );
result += "\n";

return result;
}

Expand Down Expand Up @@ -203,17 +257,18 @@ QString RicScheduleDataGenerator::generateCompdatForWell( const RimWellEventTime
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicScheduleDataGenerator::generateMswForWell( const RimWellEventTimeline& timeline,
RimEclipseCase& eclipseCase,
RimWellPath& wellPath,
const QDateTime& date )
void RicScheduleDataGenerator::generateMswForWell( const RimWellEventTimeline& timeline,
RimEclipseCase& eclipseCase,
RimWellPath& wellPath,
const QDateTime& date,
std::map<QString, QString>& keywordBlocks )
{
// Check if the well has MSW configured (from any previous tubing events applied via set_timestamp)
// If MSW is enabled, we should generate WELSEGS/COMPSEGS instead of COMPDAT
auto* mswParams = wellPath.mswCompletionParameters();
if ( !mswParams )
{
return QString();
return;
}

// Also check if there are valve or tubing events at this specific date for this well
Expand All @@ -239,7 +294,7 @@ QString RicScheduleDataGenerator::generateMswForWell( const RimWellEventTimeline
// This ensures wells only appear in schedule sections at their event dates
if ( !hasMswEvents && !hasPerfEvents )
{
return QString();
return;
}

// Extract MSW data using the existing infrastructure
Expand All @@ -251,70 +306,81 @@ QString RicScheduleDataGenerator::generateMswForWell( const RimWellEventTimeline
RicWellPathExportMswTableData::CompletionType::ALL,
date );

if ( !mswDataResult.has_value() ) return QString();
if ( !mswDataResult.has_value() ) return;

const auto& mswData = mswDataResult.value();

QString result;

// Generate WELSEGS keyword
int maxSegments = 0;
int maxBranches = 0;
Opm::DeckKeyword welsegsKw = RimKeywordFactory::welsegsKeyword( mswData, maxSegments, maxBranches );
if ( welsegsKw.isDataKeyword() || welsegsKw.size() > 0 )
{
result += RimKeywordFactory::deckKeywordToString( welsegsKw );
result += "\n";
keywordBlocks["WELSEGS"] += RimKeywordFactory::deckKeywordToString( welsegsKw ) + "\n";
}

// Generate COMPSEGS keyword
Opm::DeckKeyword compsegsKw = RimKeywordFactory::compsegsKeyword( mswData );
if ( compsegsKw.isDataKeyword() || compsegsKw.size() > 0 )
{
result += RimKeywordFactory::deckKeywordToString( compsegsKw );
result += "\n";
keywordBlocks["COMPSEGS"] += RimKeywordFactory::deckKeywordToString( compsegsKw ) + "\n";
}

// Generate WSEGVALV keyword (for valve events)
Opm::DeckKeyword wsegvalvKw = RimKeywordFactory::wsegvalvKeyword( mswData );
if ( wsegvalvKw.isDataKeyword() || wsegvalvKw.size() > 0 )
{
result += RimKeywordFactory::deckKeywordToString( wsegvalvKw );
result += "\n";
keywordBlocks["WSEGVALV"] += RimKeywordFactory::deckKeywordToString( wsegvalvKw ) + "\n";
}

// Generate WSEGAICD keyword (if AICD data present)
Opm::DeckKeyword wsegaicdKw = RimKeywordFactory::wsegaicdKeyword( mswData );
if ( wsegaicdKw.isDataKeyword() || wsegaicdKw.size() > 0 )
{
result += RimKeywordFactory::deckKeywordToString( wsegaicdKw );
result += "\n";
keywordBlocks["WSEGAICD"] += RimKeywordFactory::deckKeywordToString( wsegaicdKw ) + "\n";
}

return result;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicScheduleDataGenerator::generateWellControlForWell( const RimWellEventTimeline& timeline, const RimWellPath& well, const QDateTime& date )
void RicScheduleDataGenerator::generateWellControlForWell( const RimWellEventTimeline& timeline,
const RimWellPath& well,
const QDateTime& date,
std::map<QString, QString>& keywordBlocks )
{
// Get state and control events at this exact date for this well
auto events = timeline.getEventsAtDate( date );

QString result;

for ( auto* event : events )
{
if ( event->wellName() != well.name() ) continue;

QString keywordStr = RifEventKeywordFormatter::formatWellEvent( event, well.name() );
if ( !keywordStr.isEmpty() )
{
result += keywordStr;
result += "\n";
QString kwName = extractKeywordName( keywordStr );
if ( !kwName.isEmpty() )
{
keywordBlocks[kwName] += keywordStr + "\n";
}
}
}
}

return result;
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicScheduleDataGenerator::extractKeywordName( const QString& block )
{
const auto lines = block.split( '\n' );
for ( const auto& line : lines )
{
QString trimmed = line.trimmed();
if ( !trimmed.isEmpty() && !trimmed.startsWith( "--" ) )
{
return trimmed;
}
}
return {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <QDateTime>
#include <QString>

#include <map>
#include <vector>

class RimEclipseCase;
Expand Down Expand Up @@ -52,14 +53,26 @@ class RicScheduleDataGenerator
const std::vector<RimWellPath*>& wellPaths,
const QDateTime& date );

static QString
generateWelspecsForWell( const RimWellEventTimeline& timeline, RimEclipseCase& eclipseCase, RimWellPath& well, const QDateTime& date );

// Generate COMPDAT for a well at a specific date based on events
static QString
generateCompdatForWell( const RimWellEventTimeline& timeline, RimEclipseCase& eclipseCase, RimWellPath& well, const QDateTime& date );

// Generate WELSEGS and COMPSEGS for a well at a specific date
static QString
generateMswForWell( const RimWellEventTimeline& timeline, RimEclipseCase& eclipseCase, RimWellPath& well, const QDateTime& date );
// Generate WELSEGS and COMPSEGS for a well at a specific date, populating keyword blocks map
static void generateMswForWell( const RimWellEventTimeline& timeline,
RimEclipseCase& eclipseCase,
RimWellPath& well,
const QDateTime& date,
std::map<QString, QString>& keywordBlocks );

// Generate well control keywords (WCONPROD, WCONINJE, WELOPEN) for a well at a specific date, populating keyword blocks map
static void generateWellControlForWell( const RimWellEventTimeline& timeline,
const RimWellPath& well,
const QDateTime& date,
std::map<QString, QString>& keywordBlocks );

// Generate well control keywords (WCONPROD, WCONINJE, WELOPEN) for a well at a specific date
static QString generateWellControlForWell( const RimWellEventTimeline& timeline, const RimWellPath& well, const QDateTime& date );
// Extract keyword name from the first non-comment line of a keyword block
static QString extractKeywordName( const QString& block );
};
3 changes: 1 addition & 2 deletions GrpcInterface/RiaGrpcWellPathService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@ void populateReply( RimEclipseCase* eclipseCase, RimWellPath* wellPath, ::rips::

// Multisegment wells

int timeStep = 0;
auto mswDataContainer = RicWellPathExportMswTableData::extractSingleWellMswData( eclipseCase, wellPath, timeStep );
auto mswDataContainer = RicWellPathExportMswTableData::extractSingleWellMswData( eclipseCase, wellPath );
if ( mswDataContainer.has_value() )
{
auto tables = mswDataContainer.value();
Expand Down