From 32850cac9edb6073a4d740b3ebe16f05c1ab258f Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Thu, 6 Aug 2026 14:40:35 +0200 Subject: [PATCH] #14477 MSW: Make the ICD area summation order independent The summation reproduced the tree implementation, where an ICD sub connected to several cells took part in the sum of each of them, reading areas that earlier cells had already replaced. The result depended on the order the cells were visited in, and an ICD sub could contribute an already combined area to the next cell. Compute every cell sum from the original areas instead. An ICD sub spans a 0.1 m valve segment and normally connects to a single cell, and when it reaches into more than one it now reports the largest of the sums it takes part in. Behaviour is unchanged for the common case of one ICD sub per cell, and the sweep against TestModels/msw-export is unaffected. Added tests for the multi-cell case and for independence of the recording order. --- .../MswExport/RicMswBranchBuilder.cpp | 47 ++++++++--------- .../MswExport/RicMswBranchBuilder.h | 3 ++ .../UnitTests/RicMswBranchBuilder-Test.cpp | 50 +++++++++++++++++++ 3 files changed, 77 insertions(+), 23 deletions(-) diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/MswExport/RicMswBranchBuilder.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/MswExport/RicMswBranchBuilder.cpp index 65f1f7f648..b9f4ee7c75 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/MswExport/RicMswBranchBuilder.cpp +++ b/ApplicationLibCode/Commands/CompletionExportCommands/MswExport/RicMswBranchBuilder.cpp @@ -101,22 +101,13 @@ void applyIcdAreaPerCell( const FishbonesExportContext& context, RigMswWellExpor { if ( context.icdSegments.empty() ) return; - std::map> icdSegmentsPerCell; - for ( const auto& icdSegment : context.icdSegments ) - { - for ( auto globalCellIndex : icdSegment.globalCellIndices ) - { - icdSegmentsPerCell[globalCellIndex].insert( icdSegment.segmentNumber ); - } - } - std::set icdSegmentNumbers; for ( const auto& icdSegment : context.icdSegments ) { icdSegmentNumbers.insert( icdSegment.segmentNumber ); } - std::map areaPerSegment; + std::map originalAreaPerSegment; for ( const auto& branch : exportData.branches ) { for ( const auto& segment : branch.segments ) @@ -124,26 +115,36 @@ void applyIcdAreaPerCell( const FishbonesExportContext& context, RigMswWellExpor if ( !segment.wsegvalvData.has_value() ) continue; if ( !icdSegmentNumbers.count( segment.segmentNumber ) ) continue; - areaPerSegment[segment.segmentNumber] = segment.wsegvalvData->area; + originalAreaPerSegment[segment.segmentNumber] = segment.wsegvalvData->area; } } - // An ICD sub connected to several cells is summed once per cell, in ascending cell order, and - // ends up with the sum of the last cell it takes part in. This matches the tree implementation. - for ( const auto& [globalCellIndex, segmentNumbers] : icdSegmentsPerCell ) + // Every sum is computed from the original areas, so the result does not depend on the order the + // cells are visited in. + std::map areaSumPerCell; + for ( const auto& icdSegment : context.icdSegments ) { - double areaSum = 0.0; - for ( auto segmentNumber : segmentNumbers ) + auto it = originalAreaPerSegment.find( icdSegment.segmentNumber ); + if ( it == originalAreaPerSegment.end() ) continue; + + for ( auto globalCellIndex : icdSegment.globalCellIndices ) { - auto it = areaPerSegment.find( segmentNumber ); - if ( it != areaPerSegment.end() ) areaSum += it->second; + areaSumPerCell[globalCellIndex] += it->second; + } + } + + // An ICD sub spans a 0.1 m valve segment and normally connects to a single cell. When it reaches + // into more than one, it reports the largest of the sums it takes part in. + std::map areaPerSegment; + for ( const auto& icdSegment : context.icdSegments ) + { + double area = 0.0; + for ( auto globalCellIndex : icdSegment.globalCellIndices ) + { + area = std::max( area, areaSumPerCell[globalCellIndex] ); } - for ( auto segmentNumber : segmentNumbers ) - { - auto it = areaPerSegment.find( segmentNumber ); - if ( it != areaPerSegment.end() ) it->second = areaSum; - } + if ( area > 0.0 ) areaPerSegment[icdSegment.segmentNumber] = area; } for ( auto& branch : exportData.branches ) diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/MswExport/RicMswBranchBuilder.h b/ApplicationLibCode/Commands/CompletionExportCommands/MswExport/RicMswBranchBuilder.h index 044053f95f..6f14a15a05 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/MswExport/RicMswBranchBuilder.h +++ b/ApplicationLibCode/Commands/CompletionExportCommands/MswExport/RicMswBranchBuilder.h @@ -108,6 +108,9 @@ void applyEffectiveDiameters( const FishbonesExportContext& context, RigMswWellE //-------------------------------------------------------------------------------------------------- /// Replace the WSEGVALV area of fishbones ICD subs connected to the same grid cell with the sum of /// their areas, so that the cell sees the total flow area of the ICDs completing it. +/// +/// The sums are computed from the original areas only. An ICD sub reaching into more than one cell +/// reports the largest of the sums it takes part in. //-------------------------------------------------------------------------------------------------- void applyIcdAreaPerCell( const FishbonesExportContext& context, RigMswWellExportData& exportData ); diff --git a/ApplicationLibCode/UnitTests/RicMswBranchBuilder-Test.cpp b/ApplicationLibCode/UnitTests/RicMswBranchBuilder-Test.cpp index 7d1c09b2ef..3fadfb068d 100644 --- a/ApplicationLibCode/UnitTests/RicMswBranchBuilder-Test.cpp +++ b/ApplicationLibCode/UnitTests/RicMswBranchBuilder-Test.cpp @@ -354,6 +354,56 @@ TEST( RicMswBranchBuilder, IcdAreaPerCell_SeparateCellsAreIndependent ) EXPECT_DOUBLE_EQ( 0.003, areaOfSegment( exportData, 20 ) ); } +//-------------------------------------------------------------------------------------------------- +/// An ICD sub reaching into two cells reports the largest sum, computed from the original areas. +//-------------------------------------------------------------------------------------------------- +TEST( RicMswBranchBuilder, IcdAreaPerCell_MultipleCellsUseLargestSum ) +{ + FishbonesExportContext context; + context.icdSegments.push_back( { 10, { 100, 200 } } ); + context.icdSegments.push_back( { 20, { 100 } } ); + context.icdSegments.push_back( { 30, { 200 } } ); + context.icdSegments.push_back( { 40, { 200 } } ); + + auto exportData = makeExportData( { { 10, 0.001 }, { 20, 0.002 }, { 30, 0.003 }, { 40, 0.004 } } ); + applyIcdAreaPerCell( context, exportData ); + + // Cell 100 sums to 0.003, cell 200 to 0.008. Segment 10 takes part in both and reports the larger. + EXPECT_DOUBLE_EQ( 0.008, areaOfSegment( exportData, 10 ) ); + EXPECT_DOUBLE_EQ( 0.003, areaOfSegment( exportData, 20 ) ); + EXPECT_DOUBLE_EQ( 0.008, areaOfSegment( exportData, 30 ) ); + EXPECT_DOUBLE_EQ( 0.008, areaOfSegment( exportData, 40 ) ); +} + +//-------------------------------------------------------------------------------------------------- +/// The result does not depend on the order the ICD subs are recorded in. +//-------------------------------------------------------------------------------------------------- +TEST( RicMswBranchBuilder, IcdAreaPerCell_IsIndependentOfRecordingOrder ) +{ + FishbonesExportContext forward; + forward.icdSegments.push_back( { 10, { 100, 200 } } ); + forward.icdSegments.push_back( { 20, { 100 } } ); + forward.icdSegments.push_back( { 30, { 200 } } ); + + FishbonesExportContext reversed; + reversed.icdSegments.push_back( { 30, { 200 } } ); + reversed.icdSegments.push_back( { 20, { 100 } } ); + reversed.icdSegments.push_back( { 10, { 200, 100 } } ); + + const std::vector> segments = { { 10, 0.001 }, { 20, 0.002 }, { 30, 0.003 } }; + + auto forwardData = makeExportData( segments ); + applyIcdAreaPerCell( forward, forwardData ); + + auto reversedData = makeExportData( segments ); + applyIcdAreaPerCell( reversed, reversedData ); + + for ( int segmentNumber : { 10, 20, 30 } ) + { + EXPECT_DOUBLE_EQ( areaOfSegment( forwardData, segmentNumber ), areaOfSegment( reversedData, segmentNumber ) ); + } +} + //-------------------------------------------------------------------------------------------------- /// Valve segments that are not fishbones ICD subs are left alone. //--------------------------------------------------------------------------------------------------