mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-26 07:00:17 -05:00
#13425 Fix WELSEGS date scoping: tubing events only appear at their event dates
Tubing event diameter/roughness values were leaking into earlier date sections in the generated schedule. The WELSEGS collection functions did not filter intervals by export date, unlike COMPSEGS and other keywords which already received the exportDate parameter. Thread exportDate through collectWelsegsData, collectWelsegsDataRecursively, and collectWelsegsSegment. Add date-aware overloads of getDiameterAtMD and getRoughnessAtMD that use isActiveOnDate to filter intervals. Update test to verify tubing values only appear at their scheduled dates.
This commit is contained in:
1 parent
6782d6a40e
commit
82e2dbc434
8 files changed
+186
-24
No files matched your search
@@ -41,7 +41,8 @@ void RicMswTableDataTools::collectWelsegsData( RigMswTableData&
|
||||
RicMswExportInfo& exportInfo,
|
||||
double maxSegmentLength,
|
||||
const std::vector<std::pair<double, double>>& customSegmentIntervals,
|
||||
bool exportCompletionSegmentsAfterMainBore )
|
||||
bool exportCompletionSegmentsAfterMainBore,
|
||||
const std::optional<QDateTime>& exportDate )
|
||||
{
|
||||
// Set up WELSEGS header
|
||||
WelsegsHeader header;
|
||||
@@ -68,7 +69,8 @@ void RicMswTableDataTools::collectWelsegsData( RigMswTableData&
|
||||
maxSegmentLength,
|
||||
customSegmentIntervals,
|
||||
exportCompletionSegmentsAfterMainBore,
|
||||
nullptr );
|
||||
nullptr,
|
||||
exportDate );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -80,8 +82,9 @@ void RicMswTableDataTools::collectWelsegsDataRecursively( RigMswTableData&
|
||||
gsl::not_null<int*> segmentNumber,
|
||||
double maxSegmentLength,
|
||||
const std::vector<std::pair<double, double>>& customSegmentIntervals,
|
||||
bool exportCompletionSegmentsAfterMainBore,
|
||||
RicMswSegment* connectedToSegment )
|
||||
bool exportCompletionSegmentsAfterMainBore,
|
||||
RicMswSegment* connectedToSegment,
|
||||
const std::optional<QDateTime>& exportDate )
|
||||
{
|
||||
auto outletSegment = connectedToSegment;
|
||||
RicMswValve* outletValve = nullptr;
|
||||
@@ -113,7 +116,16 @@ void RicMswTableDataTools::collectWelsegsDataRecursively( RigMswTableData&
|
||||
branchDescription = QString( "Segments on branch %1" ).arg( branch->label() );
|
||||
}
|
||||
|
||||
collectWelsegsSegment( tableData, segment, outletSegment, exportInfo, maxSegmentLength, customSegmentIntervals, branch, segmentNumber, branchDescription );
|
||||
collectWelsegsSegment( tableData,
|
||||
segment,
|
||||
outletSegment,
|
||||
exportInfo,
|
||||
maxSegmentLength,
|
||||
customSegmentIntervals,
|
||||
branch,
|
||||
segmentNumber,
|
||||
branchDescription,
|
||||
exportDate );
|
||||
outletSegment = segment;
|
||||
|
||||
if ( !exportCompletionSegmentsAfterMainBore )
|
||||
@@ -161,7 +173,8 @@ void RicMswTableDataTools::collectWelsegsDataRecursively( RigMswTableData&
|
||||
maxSegmentLength,
|
||||
customSegmentIntervals,
|
||||
exportCompletionSegmentsAfterMainBore,
|
||||
outletSegmentForChildBranch );
|
||||
outletSegmentForChildBranch,
|
||||
exportDate );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +189,8 @@ void RicMswTableDataTools::collectWelsegsSegment( RigMswTableData&
|
||||
const std::vector<std::pair<double, double>>& customSegmentIntervals,
|
||||
gsl::not_null<RicMswBranch*> branch,
|
||||
int* segmentNumber,
|
||||
QString branchDescription )
|
||||
QString branchDescription,
|
||||
const std::optional<QDateTime>& exportDate )
|
||||
{
|
||||
CVF_ASSERT( segment && segmentNumber );
|
||||
|
||||
@@ -227,8 +241,19 @@ void RicMswTableDataTools::collectWelsegsSegment( RigMswTableData&
|
||||
length = midPointMD;
|
||||
}
|
||||
|
||||
const auto linerDiameter = branch->wellPath()->mswCompletionParameters()->getDiameterAtMD( midPointMD, exportInfo.unitSystem() );
|
||||
const auto roughnessFactor = branch->wellPath()->mswCompletionParameters()->getRoughnessAtMD( midPointMD, exportInfo.unitSystem() );
|
||||
double linerDiameter = 0.0;
|
||||
double roughnessFactor = 0.0;
|
||||
if ( exportDate.has_value() )
|
||||
{
|
||||
linerDiameter = branch->wellPath()->mswCompletionParameters()->getDiameterAtMD( midPointMD, exportInfo.unitSystem(), *exportDate );
|
||||
roughnessFactor =
|
||||
branch->wellPath()->mswCompletionParameters()->getRoughnessAtMD( midPointMD, exportInfo.unitSystem(), *exportDate );
|
||||
}
|
||||
else
|
||||
{
|
||||
linerDiameter = branch->wellPath()->mswCompletionParameters()->getDiameterAtMD( midPointMD, exportInfo.unitSystem() );
|
||||
roughnessFactor = branch->wellPath()->mswCompletionParameters()->getRoughnessAtMD( midPointMD, exportInfo.unitSystem() );
|
||||
}
|
||||
|
||||
WelsegsRow row;
|
||||
row.sourceWellName = branch->wellPath()->name().toStdString();
|
||||
|
||||
@@ -112,7 +112,8 @@ void collectWelsegsData( RigMswTableData& tableData
|
||||
RicMswExportInfo& exportInfo,
|
||||
double maxSegmentLength,
|
||||
const std::vector<std::pair<double, double>>& customSegmentIntervals,
|
||||
bool exportCompletionSegmentsAfterMainBore );
|
||||
bool exportCompletionSegmentsAfterMainBore,
|
||||
const std::optional<QDateTime>& exportDate = std::nullopt );
|
||||
|
||||
void collectWelsegsDataRecursively( RigMswTableData& tableData,
|
||||
RicMswExportInfo& exportInfo,
|
||||
@@ -121,7 +122,8 @@ void collectWelsegsDataRecursively( RigMswTableData&
|
||||
double maxSegmentLength,
|
||||
const std::vector<std::pair<double, double>>& customSegmentIntervals,
|
||||
bool exportCompletionSegmentsAfterMainBore,
|
||||
RicMswSegment* connectedToSegment );
|
||||
RicMswSegment* connectedToSegment,
|
||||
const std::optional<QDateTime>& exportDate = std::nullopt );
|
||||
|
||||
void collectCompsegData( RigMswTableData& tableData,
|
||||
RicMswExportInfo& exportInfo,
|
||||
@@ -156,7 +158,8 @@ void collectWelsegsSegment( RigMswTableData& tableD
|
||||
const std::vector<std::pair<double, double>>& customSegmentIntervals,
|
||||
gsl::not_null<RicMswBranch*> branch,
|
||||
int* segmentNumber,
|
||||
QString branchDescription );
|
||||
QString branchDescription,
|
||||
const std::optional<QDateTime>& exportDate = std::nullopt );
|
||||
|
||||
void collectValveWelsegsSegment( RigMswTableData& tableData,
|
||||
const RicMswSegment* outletSegment,
|
||||
|
||||
+2
-1
@@ -127,7 +127,8 @@ std::expected<RigMswTableData, std::string>
|
||||
exportInfo,
|
||||
mswParameters->maxSegmentLength(),
|
||||
customSegmentIntervals,
|
||||
exportCompletionsAfterMainBoreSegments );
|
||||
exportCompletionsAfterMainBoreSegments,
|
||||
exportDate );
|
||||
|
||||
{
|
||||
// Get COMPSEGS for main grid
|
||||
|
||||
+38
@@ -90,6 +90,44 @@ double RimDiameterRoughnessIntervalCollection::getRoughnessAtMD( double md, RiaD
|
||||
return RimMswCompletionParameters::defaultRoughnessFactor( unitSystem );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RimDiameterRoughnessIntervalCollection::getDiameterAtMD( double md,
|
||||
RiaDefines::EclipseUnitSystem unitSystem,
|
||||
const QDateTime& exportDate ) const
|
||||
{
|
||||
for ( auto* interval : intervals() )
|
||||
{
|
||||
if ( interval && interval->containsMD( md ) && interval->isActiveOnDate( exportDate ) )
|
||||
{
|
||||
return interval->diameter( unitSystem );
|
||||
}
|
||||
}
|
||||
|
||||
// Return default if no active interval found at this date
|
||||
return RimMswCompletionParameters::defaultLinerDiameter( unitSystem );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RimDiameterRoughnessIntervalCollection::getRoughnessAtMD( double md,
|
||||
RiaDefines::EclipseUnitSystem unitSystem,
|
||||
const QDateTime& exportDate ) const
|
||||
{
|
||||
for ( auto* interval : intervals() )
|
||||
{
|
||||
if ( interval && interval->containsMD( md ) && interval->isActiveOnDate( exportDate ) )
|
||||
{
|
||||
return interval->roughnessFactor( unitSystem );
|
||||
}
|
||||
}
|
||||
|
||||
// Return default if no active interval found at this date
|
||||
return RimMswCompletionParameters::defaultRoughnessFactor( unitSystem );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
+6
@@ -21,6 +21,8 @@
|
||||
#include "RimDiameterRoughnessInterval.h"
|
||||
#include "RimIntervalCollection.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
/// Collection to manage diameter and roughness intervals for measured depth ranges
|
||||
@@ -41,6 +43,10 @@ public:
|
||||
double getDiameterAtMD( double md, RiaDefines::EclipseUnitSystem unitSystem ) const;
|
||||
double getRoughnessAtMD( double md, RiaDefines::EclipseUnitSystem unitSystem ) const;
|
||||
|
||||
// Date-aware lookup methods (only returns values from intervals active on the given date)
|
||||
double getDiameterAtMD( double md, RiaDefines::EclipseUnitSystem unitSystem, const QDateTime& exportDate ) const;
|
||||
double getRoughnessAtMD( double md, RiaDefines::EclipseUnitSystem unitSystem, const QDateTime& exportDate ) const;
|
||||
|
||||
// Domain-specific validation
|
||||
bool coversFullRange( double startMD, double endMD ) const;
|
||||
|
||||
|
||||
@@ -295,6 +295,34 @@ double RimMswCompletionParameters::getRoughnessAtMD( double md, RiaDefines::Ecli
|
||||
return roughnessFactor( unitSystem );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RimMswCompletionParameters::getDiameterAtMD( double md, RiaDefines::EclipseUnitSystem unitSystem, const QDateTime& exportDate ) const
|
||||
{
|
||||
if ( isUsingIntervalSpecificValues() && m_diameterRoughnessIntervals() )
|
||||
{
|
||||
return m_diameterRoughnessIntervals()->getDiameterAtMD( md, unitSystem, exportDate );
|
||||
}
|
||||
|
||||
// Fall back to single value
|
||||
return linerDiameter( unitSystem );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RimMswCompletionParameters::getRoughnessAtMD( double md, RiaDefines::EclipseUnitSystem unitSystem, const QDateTime& exportDate ) const
|
||||
{
|
||||
if ( isUsingIntervalSpecificValues() && m_diameterRoughnessIntervals() )
|
||||
{
|
||||
return m_diameterRoughnessIntervals()->getRoughnessAtMD( md, unitSystem, exportDate );
|
||||
}
|
||||
|
||||
// Fall back to single value
|
||||
return roughnessFactor( unitSystem );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "cafPdmObject.h"
|
||||
#include "cafPdmUiGroup.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
class RimDiameterRoughnessIntervalCollection;
|
||||
class RimCustomSegmentIntervalCollection;
|
||||
|
||||
@@ -92,6 +94,10 @@ public:
|
||||
double getDiameterAtMD( double md, RiaDefines::EclipseUnitSystem unitSystem ) const;
|
||||
double getRoughnessAtMD( double md, RiaDefines::EclipseUnitSystem unitSystem ) const;
|
||||
|
||||
// Date-aware overloads (only returns values from intervals active on the given date)
|
||||
double getDiameterAtMD( double md, RiaDefines::EclipseUnitSystem unitSystem, const QDateTime& exportDate ) const;
|
||||
double getRoughnessAtMD( double md, RiaDefines::EclipseUnitSystem unitSystem, const QDateTime& exportDate ) const;
|
||||
|
||||
RimDiameterRoughnessIntervalCollection* diameterRoughnessIntervals() const;
|
||||
|
||||
// Custom segment intervals
|
||||
|
||||
@@ -515,10 +515,12 @@ class TestScheduleGeneration:
|
||||
Scenario:
|
||||
- Add tubing + perf event for well A at 2024-01-01 (depth 2000-2200)
|
||||
- Add tubing + perf event for well B at 2024-03-01 (depth 1500-1700)
|
||||
- Add another perf event for well A at 2024-04-01 (depth 1800-2000, different depth)
|
||||
- Add another tubing + perf event for well A at 2024-04-01 (depth 1800-2000, different diameter)
|
||||
- Set timestamp to 2024-12-31 (after all events)
|
||||
- Verify well A appears in January and April sections with different COMPSEGS
|
||||
- Verify well B appears in March section but NOT in January section
|
||||
- Verify each perf event has distinct diameter and skin_factor values
|
||||
- Verify April WELSEGS has different inner_diameter than January WELSEGS
|
||||
"""
|
||||
project, case, timeline = project_with_case_and_well
|
||||
|
||||
@@ -531,10 +533,10 @@ class TestScheduleGeneration:
|
||||
timeline.add_tubing_event(
|
||||
event_date="2024-01-01",
|
||||
well_path=well_path_a,
|
||||
start_md=0.0,
|
||||
end_md=2400.0,
|
||||
inner_diameter=0.15,
|
||||
roughness=1.0e-5,
|
||||
start_md=1500.0,
|
||||
end_md=2300.0,
|
||||
inner_diameter=0.22,
|
||||
roughness=9.0e-5,
|
||||
)
|
||||
timeline.add_perf_event(
|
||||
event_date="2024-01-01",
|
||||
@@ -550,28 +552,36 @@ class TestScheduleGeneration:
|
||||
timeline.add_tubing_event(
|
||||
event_date="2024-03-01",
|
||||
well_path=well_path_b,
|
||||
start_md=0.0,
|
||||
start_md=1400.0,
|
||||
end_md=2000.0,
|
||||
inner_diameter=0.15,
|
||||
roughness=1.0e-5,
|
||||
roughness=2.0e-5,
|
||||
)
|
||||
timeline.add_perf_event(
|
||||
event_date="2024-03-01",
|
||||
well_path=well_path_b,
|
||||
start_md=1500.0,
|
||||
end_md=1700.0,
|
||||
diameter=0.1,
|
||||
skin_factor=0.5,
|
||||
diameter=0.2,
|
||||
skin_factor=0.8,
|
||||
state="OPEN",
|
||||
)
|
||||
|
||||
# Add another perforation for well A at 2024-04-01 at a different depth (1800-2000)
|
||||
# Add another tubing + perforation for well A at 2024-04-01 at a different depth (1800-2000)
|
||||
timeline.add_tubing_event(
|
||||
event_date="2024-04-01",
|
||||
well_path=well_path_a,
|
||||
start_md=600.0,
|
||||
end_md=2400.0,
|
||||
inner_diameter=0.20,
|
||||
roughness=3.0e-5,
|
||||
)
|
||||
timeline.add_perf_event(
|
||||
event_date="2024-04-01",
|
||||
well_path=well_path_a,
|
||||
start_md=1800.0,
|
||||
end_md=2000.0,
|
||||
diameter=0.1,
|
||||
diameter=0.15,
|
||||
skin_factor=0.3,
|
||||
state="OPEN",
|
||||
)
|
||||
@@ -686,11 +696,56 @@ class TestScheduleGeneration:
|
||||
"April COMPSEGS should contain perforation range 1800-2000"
|
||||
)
|
||||
|
||||
# Verify WELSEGS sections exist for tubing events
|
||||
assert "WELSEGS" in january_section, (
|
||||
"January section should contain WELSEGS for well A (tubing event)"
|
||||
)
|
||||
assert "WELSEGS" in april_section, (
|
||||
"April section should contain WELSEGS for well A (second tubing event)"
|
||||
)
|
||||
|
||||
# Verify that the April tubing event values do NOT leak into January WELSEGS.
|
||||
# January tubing: inner_diameter=0.15, roughness=9e-05
|
||||
# April tubing: inner_diameter=0.20, roughness=3e-05
|
||||
# The roughness value 3e-05 is unique to the April tubing event,
|
||||
# so it must NOT appear in the January section.
|
||||
assert "3e-05" not in january_section, (
|
||||
"January WELSEGS should NOT contain roughness 3e-05 from April tubing event. "
|
||||
"Tubing events should only appear at their event date, not earlier."
|
||||
)
|
||||
|
||||
# Verify April section contains the April tubing roughness
|
||||
assert "3e-05" in april_section, (
|
||||
"April WELSEGS should contain roughness 3e-05 from April tubing event"
|
||||
)
|
||||
|
||||
# Verify distinct perf diameter/skin values appear in COMPDAT sections
|
||||
# January perf: diameter=0.1, skin_factor=0.5
|
||||
assert "COMPDAT" in january_section, (
|
||||
"January section should contain COMPDAT for well A"
|
||||
)
|
||||
assert "0.5" in january_section, (
|
||||
"January COMPDAT should contain skin_factor 0.5 from first perf event"
|
||||
)
|
||||
|
||||
# April perf: diameter=0.15, skin_factor=0.3
|
||||
assert "COMPDAT" in april_section, (
|
||||
"April section should contain COMPDAT for well A"
|
||||
)
|
||||
assert "0.3" in april_section, (
|
||||
"April COMPDAT should contain skin_factor 0.3 from third perf event"
|
||||
)
|
||||
|
||||
print("\n✓ Verified: Well A appears in January section (event at 2000-2200)")
|
||||
print("✓ Verified: Well B does NOT appear in January section")
|
||||
print("✓ Verified: Well B appears in March section (its event date)")
|
||||
print("✓ Verified: Well A appears in April section (event at 1800-2000)")
|
||||
print("✓ Verified: COMPSEGS for well A is different in January vs April")
|
||||
print(
|
||||
"✓ Verified: WELSEGS present in both January and April (two tubing events)"
|
||||
)
|
||||
print("✓ Verified: April tubing values do NOT leak into January section")
|
||||
print("✓ Verified: Distinct perf diameters/skin values in COMPDAT sections")
|
||||
|
||||
def test_example_workflow_schedule_generation(self, project_with_case_and_well):
|
||||
"""Test the exact workflow from well_event_schedule.py example.
|
||||
|
||||
Reference in new issue
Block a user