#14958 Sim Events: Move PRESSURE_COMPONENTS to WELSPECS

Move Pressure Drop and Length and Depth from the segment collection to
the well path completion settings. PRESSURE_COMPONENTS is now a dated
WELSPECS attribute used for the WELSEGS header at export, and is
rejected on SEGMENT events.
This commit is contained in:
Kristian Bendiksen committed 2026-10-19 09:28:29 +02:00
1 parent 2f86d4c210
commit c56e921160
21 files changed
+326 -186

No files matched your search

@@ -83,7 +83,7 @@ std::vector<RigMswBranch> buildLateralBranches( RimEclipseCase*
auto mswParameters = wellPath->segmentCollection();
if ( !mswParameters ) return result;
const std::string infoType = mswParameters->lengthAndDepth().text().toStdString();
const std::string infoType = wellPath->completionSettings()->lengthAndDepth().text().toStdString();
const std::vector<std::pair<double, double>> segmentIntervals = mswParameters->getSegmentIntervals();
const double tieInMD = wellPath->wellPathTieIn()->tieInMeasuredDepth();
const double tieInTVD = -wellPath->wellPathGeometry()->interpolatedPointAlongWellPath( tieInMD ).z();
@@ -313,7 +313,7 @@ RigMswWellExportData buildMswWellExportData( RimEclipseCase*
const RiaDefines::EclipseUnitSystem unitSystem = eclipseCase->eclipseCaseData()->unitsType();
const RigMainGrid* mainGrid = eclipseCase->mainGrid();
const std::string infoType = mswParameters->lengthAndDepth().text().toStdString();
const std::string infoType = wellPath->completionSettings()->lengthAndDepth().text().toStdString();
auto cellIntersections = RicWellPathExportMswTableData::generateCellSegments( eclipseCase, wellPath );
double initialMD = RicWellPathExportMswTableData::computeIntitialMeasuredDepth( eclipseCase, wellPath, mswParameters, cellIntersections );
@@ -327,7 +327,7 @@ RigMswWellExportData buildMswWellExportData( RimEclipseCase*
header.topLength = initialMD;
header.topDepth = initialTVD;
header.infoType = infoType;
header.pressureComponents = mswParameters->pressureDrop().text().toStdString();
header.pressureComponents = wellPath->completionSettings()->pressureDrop().text().toStdString();
const bool includePerforations = ( completionType & CompletionType::PERFORATIONS ) == CompletionType::PERFORATIONS;
const std::vector<const RimPerforationInterval*> perforationIntervals =
@@ -66,6 +66,37 @@ bool eventBelongsToWell( const RimWellEvent& event, const RimWellPath& well )
const RimWellPath* eventWell = event.wellPath();
return eventWell && exportNameForWell( *eventWell ) == exportNameForWell( well );
}
// WELSPECS settings in effect for a well at a date: the latest WELSPECS event at or before the date, otherwise the
// baseline captured by the first event. Returns nullopt when the well has no WELSPECS events.
std::optional<RimWellSpecData> wellSpecDataAtDate( const RimWellEventTimeline& timeline, const RimWellPath& well, const QDateTime& date )
{
const RimWellEventWellSpec* firstWellSpec = nullptr;
const RimWellEventWellSpec* latestWellSpec = nullptr;
for ( auto* event : timeline.events() )
{
if ( event->eventType() != RimWellEvent::EventType::WELLSPEC || !eventBelongsToWell( *event, well ) ) continue;
auto* wellSpec = dynamic_cast<RimWellEventWellSpec*>( event );
if ( !wellSpec ) continue;
if ( !firstWellSpec || wellSpec->eventDate() < firstWellSpec->eventDate() ) firstWellSpec = wellSpec;
if ( wellSpec->eventDate() <= date && ( !latestWellSpec || wellSpec->eventDate() > latestWellSpec->eventDate() ) )
{
latestWellSpec = wellSpec;
}
}
std::optional<RimWellSpecData> wellSpecData;
if ( latestWellSpec )
{
wellSpecData = latestWellSpec->wellSpecData();
}
else if ( firstWellSpec )
{
wellSpecData = firstWellSpec->baselineData();
}
return wellSpecData;
}
} // namespace
//--------------------------------------------------------------------------------------------------
@@ -388,7 +419,7 @@ std::expected<QString, QString> RicScheduleDataGenerator::generateDateSection( c
// MSW keywords describe the shared well tree, including all its laterals.
if ( mswIt != group.end() )
{
generateMswForWell( eclipseCase, *( *mswIt )->topLevelWellPath(), date, events, keywordBlocks, unmergedBlocks, emittedSegmentLayouts );
generateMswForWell( timeline, eclipseCase, *( *mswIt )->topLevelWellPath(), date, events, keywordBlocks, unmergedBlocks, emittedSegmentLayouts );
}
for ( auto* well : group )
@@ -561,30 +592,7 @@ std::optional<Opm::DeckKeyword> RicScheduleDataGenerator::generateWelspecsForWel
if ( !hasEvents ) return std::nullopt;
const RimWellEventWellSpec* firstWellSpec = nullptr;
const RimWellEventWellSpec* latestWellSpec = nullptr;
for ( auto* event : timeline.events() )
{
if ( event->eventType() != RimWellEvent::EventType::WELLSPEC || !eventBelongsToWell( *event, well ) ) continue;
auto* wellSpec = dynamic_cast<RimWellEventWellSpec*>( event );
if ( !wellSpec ) continue;
if ( !firstWellSpec || wellSpec->eventDate() < firstWellSpec->eventDate() ) firstWellSpec = wellSpec;
if ( wellSpec->eventDate() <= date && ( !latestWellSpec || wellSpec->eventDate() > latestWellSpec->eventDate() ) )
{
latestWellSpec = wellSpec;
}
}
std::optional<RimWellSpecData> wellSpecData;
if ( latestWellSpec )
{
wellSpecData = latestWellSpec->wellSpecData();
}
else if ( firstWellSpec )
{
wellSpecData = firstWellSpec->baselineData();
}
const auto wellSpecData = wellSpecDataAtDate( timeline, well, date );
std::string wellGroupName = well.completionSettings()->groupNameForExport().toStdString();
auto welspecsKw = RimKeywordFactory::welspecsKeyword( wellGroupName, &eclipseCase, &well, wellSpecData ? &wellSpecData.value() : nullptr );
@@ -639,7 +647,8 @@ void RicScheduleDataGenerator::generateCompletionsForWellGroup( RimEclipseCase&
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicScheduleDataGenerator::generateMswForWell( RimEclipseCase& eclipseCase,
void RicScheduleDataGenerator::generateMswForWell( const RimWellEventTimeline& timeline,
RimEclipseCase& eclipseCase,
RimWellPath& wellPath,
const QDateTime& date,
const std::vector<RimWellEvent*>& eventsAtDate,
@@ -684,7 +693,15 @@ void RicScheduleDataGenerator::generateMswForWell( RimEclipseCase&
if ( !mswDataResult.has_value() ) return;
const auto& mswData = mswDataResult.value();
auto& mswData = mswDataResult.value();
// The pressure drop components are a WELSPECS event setting, so they can change over time.
if ( const auto wellSpecData = wellSpecDataAtDate( timeline, wellPath, date ) )
{
auto header = mswData.welsegsHeader();
header.pressureComponents = RimWellPathCompletionSettings::PressureDropEnum( wellSpecData->pressureDrop ).text().toStdString();
mswData.setWelsegsHeader( header );
}
// WELSEGS and COMPSEGS are not merged across wells (see generateDateSection): each well keeps
// its own block, appended in well order.
@@ -111,7 +111,9 @@ private:
// The caller decides which wells get MSW keywords (see mswWells in generateSchedule).
// WELSEGS / COMPSEGS are written for segment/perforation events, and for valve events when the
// segment layout differs from the one last written for the well (tracked in emittedSegmentLayouts).
static void generateMswForWell( RimEclipseCase& eclipseCase,
// The WELSEGS pressure drop components come from the WELSPECS event in effect at the date.
static void generateMswForWell( const RimWellEventTimeline& timeline,
RimEclipseCase& eclipseCase,
RimWellPath& well,
const QDateTime& date,
const std::vector<RimWellEvent*>& eventsAtDate,