diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.cpp index c1bbbb8873..8cdcc01398 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.cpp +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.cpp @@ -41,7 +41,8 @@ void RicMswTableDataTools::collectWelsegsData( RigMswTableData& RicMswExportInfo& exportInfo, double maxSegmentLength, const std::vector>& customSegmentIntervals, - bool exportCompletionSegmentsAfterMainBore ) + bool exportCompletionSegmentsAfterMainBore, + const std::optional& 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 segmentNumber, double maxSegmentLength, const std::vector>& customSegmentIntervals, - bool exportCompletionSegmentsAfterMainBore, - RicMswSegment* connectedToSegment ) + bool exportCompletionSegmentsAfterMainBore, + RicMswSegment* connectedToSegment, + const std::optional& 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>& customSegmentIntervals, gsl::not_null branch, int* segmentNumber, - QString branchDescription ) + QString branchDescription, + const std::optional& 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(); diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.h b/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.h index 05ec847d98..50e5eeb034 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.h +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.h @@ -112,7 +112,8 @@ void collectWelsegsData( RigMswTableData& tableData RicMswExportInfo& exportInfo, double maxSegmentLength, const std::vector>& customSegmentIntervals, - bool exportCompletionSegmentsAfterMainBore ); + bool exportCompletionSegmentsAfterMainBore, + const std::optional& exportDate = std::nullopt ); void collectWelsegsDataRecursively( RigMswTableData& tableData, RicMswExportInfo& exportInfo, @@ -121,7 +122,8 @@ void collectWelsegsDataRecursively( RigMswTableData& double maxSegmentLength, const std::vector>& customSegmentIntervals, bool exportCompletionSegmentsAfterMainBore, - RicMswSegment* connectedToSegment ); + RicMswSegment* connectedToSegment, + const std::optional& exportDate = std::nullopt ); void collectCompsegData( RigMswTableData& tableData, RicMswExportInfo& exportInfo, @@ -156,7 +158,8 @@ void collectWelsegsSegment( RigMswTableData& tableD const std::vector>& customSegmentIntervals, gsl::not_null branch, int* segmentNumber, - QString branchDescription ); + QString branchDescription, + const std::optional& exportDate = std::nullopt ); void collectValveWelsegsSegment( RigMswTableData& tableData, const RicMswSegment* outletSegment, diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportMswTableData.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportMswTableData.cpp index cb5dd1d320..fd7cb66fae 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportMswTableData.cpp +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportMswTableData.cpp @@ -127,7 +127,8 @@ std::expected exportInfo, mswParameters->maxSegmentLength(), customSegmentIntervals, - exportCompletionsAfterMainBoreSegments ); + exportCompletionsAfterMainBoreSegments, + exportDate ); { // Get COMPSEGS for main grid diff --git a/ApplicationLibCode/ProjectDataModel/Completions/RimDiameterRoughnessIntervalCollection.cpp b/ApplicationLibCode/ProjectDataModel/Completions/RimDiameterRoughnessIntervalCollection.cpp index b7ab7d7283..ce62653177 100644 --- a/ApplicationLibCode/ProjectDataModel/Completions/RimDiameterRoughnessIntervalCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/Completions/RimDiameterRoughnessIntervalCollection.cpp @@ -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 ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/Completions/RimDiameterRoughnessIntervalCollection.h b/ApplicationLibCode/ProjectDataModel/Completions/RimDiameterRoughnessIntervalCollection.h index 2158476e14..5cca641aa4 100644 --- a/ApplicationLibCode/ProjectDataModel/Completions/RimDiameterRoughnessIntervalCollection.h +++ b/ApplicationLibCode/ProjectDataModel/Completions/RimDiameterRoughnessIntervalCollection.h @@ -21,6 +21,8 @@ #include "RimDiameterRoughnessInterval.h" #include "RimIntervalCollection.h" +#include + //================================================================================================== /// /// 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; diff --git a/ApplicationLibCode/ProjectDataModel/Completions/RimMswCompletionParameters.cpp b/ApplicationLibCode/ProjectDataModel/Completions/RimMswCompletionParameters.cpp index 741175eb78..6e1f654b71 100644 --- a/ApplicationLibCode/ProjectDataModel/Completions/RimMswCompletionParameters.cpp +++ b/ApplicationLibCode/ProjectDataModel/Completions/RimMswCompletionParameters.cpp @@ -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 ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/Completions/RimMswCompletionParameters.h b/ApplicationLibCode/ProjectDataModel/Completions/RimMswCompletionParameters.h index 3c5d35021a..476f0df770 100644 --- a/ApplicationLibCode/ProjectDataModel/Completions/RimMswCompletionParameters.h +++ b/ApplicationLibCode/ProjectDataModel/Completions/RimMswCompletionParameters.h @@ -24,6 +24,8 @@ #include "cafPdmObject.h" #include "cafPdmUiGroup.h" +#include + 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 diff --git a/GrpcInterface/Python/rips/tests/test_well_events.py b/GrpcInterface/Python/rips/tests/test_well_events.py index dc1f89f8ea..eab8a56f40 100644 --- a/GrpcInterface/Python/rips/tests/test_well_events.py +++ b/GrpcInterface/Python/rips/tests/test_well_events.py @@ -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.