From 97d132685bd8c9db9bfb9400401a38a578f29c7e Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Mon, 12 Oct 2026 15:22:10 +0200 Subject: [PATCH] #14804: Address review comments on segment placement, ISEG gRPC, min/max validation --- ApplicationExeCode/Resources/ResInsight.qrc | 2 +- .../MswExport/RicMswBranchBuilder.cpp | 10 ++++++---- .../RicMswTableDataTools.cpp | 12 +++++++----- .../RicMswTableDataTools.h | 11 ++++++----- .../Completions/RimSegmentInterval.cpp | 10 +++++++++- .../UnitTests/RicMswBranchBuilder-Test.cpp | 14 ++++++++++++++ GrpcInterface/GrpcProtos/SimulatorTables.proto | 1 + GrpcInterface/RiaWellPathDataToGrpcConverter.cpp | 5 +++++ 8 files changed, 49 insertions(+), 16 deletions(-) diff --git a/ApplicationExeCode/Resources/ResInsight.qrc b/ApplicationExeCode/Resources/ResInsight.qrc index 4589087e25..3abfdfc05d 100644 --- a/ApplicationExeCode/Resources/ResInsight.qrc +++ b/ApplicationExeCode/Resources/ResInsight.qrc @@ -111,7 +111,6 @@ Parallel.svg PerforationInterval16x16.png PerforationIntervals16x16.png - Segment.svg Perspective.svg PlotWindow.svg Plus.png @@ -134,6 +133,7 @@ RulerPoly.svg Save.svg SaveAs.svg + Segment.svg SnapShot.svg SnapShotSave.svg SnapShotSaveViews.svg diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/MswExport/RicMswBranchBuilder.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/MswExport/RicMswBranchBuilder.cpp index 5bb1983d41..32c864c644 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/MswExport/RicMswBranchBuilder.cpp +++ b/ApplicationLibCode/Commands/CompletionExportCommands/MswExport/RicMswBranchBuilder.cpp @@ -199,7 +199,7 @@ std::optional //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -double segmentMidpointMD( const std::vector& cellSegMap, int segmentNumber ) +double segmentNodeMD( const std::vector& cellSegMap, int segmentNumber ) { for ( const auto& entry : cellSegMap ) { @@ -487,7 +487,8 @@ RigMswBranch buildMainBoreBranch( const RimWellPath* std::vector nodes = RicMswTableDataTools::placeSegmentNodes( heelMD, candidateNodes, segmentationSettings.intervals, - cellPieces.empty() ? heelMD : cellPieces.back().second ); + cellPieces.empty() ? heelMD : cellPieces.back().second, + cellPieces.empty() ? heelMD : cellPieces.front().first ); if ( !cellPieces.empty() && !perforationIntervals.empty() && ( segmentationSettings.singleSegmentBeforeFirstPerforation || segmentationSettings.singleSegmentAfterLastPerforation ) ) @@ -496,6 +497,7 @@ RigMswBranch buildMainBoreBranch( const RimWellPath* double lastPerforationMD = -std::numeric_limits::infinity(); for ( const auto* perf : perforationIntervals ) { + if ( exportDate && !perf->isActiveOnDate( *exportDate ) ) continue; firstPerforationMD = std::min( firstPerforationMD, perf->startMD() ); lastPerforationMD = std::max( lastPerforationMD, perf->endMD() ); } @@ -686,7 +688,7 @@ std::vector buildValveBranches( const RimWellPath* { const int cellBranch = ++branchNumber; const int outletSeg = findContainingSegmentForMD( cellSegMap, entry.ci.distanceStart ); - const double valveMD = segmentMidpointMD( cellSegMap, outletSeg ); + const double valveMD = segmentNodeMD( cellSegMap, outletSeg ); const double valveEndMD = valveMD + RicMswTableDataTools::valveSegmentLength; const double startTVD = RicMswTableDataTools::tvdFromMeasuredDepth( wellPath, valveMD ); const double endTVD = RicMswTableDataTools::tvdFromMeasuredDepth( wellPath, valveEndMD ); @@ -770,7 +772,7 @@ std::vector buildValveBranches( const RimWellPath* { const int cellBranch = ++branchNumber; const int outletSeg = findContainingSegmentForMD( cellSegMap, entry.ci.distanceStart ); - const double valveMD = segmentMidpointMD( cellSegMap, outletSeg ); + const double valveMD = segmentNodeMD( cellSegMap, outletSeg ); const double valveEndMD = valveMD + RicMswTableDataTools::valveSegmentLength; const double startTVD = RicMswTableDataTools::tvdFromMeasuredDepth( wellPath, valveMD ); const double endTVD = RicMswTableDataTools::tvdFromMeasuredDepth( wellPath, valveEndMD ); diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.cpp b/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.cpp index de9241c10c..0ca6efc2c5 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.cpp +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.cpp @@ -188,7 +188,8 @@ const RicMswTableDataTools::SegmentationInterval* intervalAtMD( const std::vecto std::vector RicMswTableDataTools::placeSegmentNodes( double outletMD, const std::vector& allCandidateNodes, const std::vector& intervals, - double lastMD ) + double lastMD, + double firstMD ) { if ( intervals.empty() ) return allCandidateNodes; @@ -209,7 +210,7 @@ std::vector RicMswTableDataTools::placeSegmentNodes( double const double endMD = std::min( interval.endMD, lastMD ); for ( int k = 0;; ++k ) { - const double segmentStart = std::max( interval.startMD + k * length, outletMD ); + const double segmentStart = std::max( { interval.startMD + k * length, outletMD, firstMD } ); const double segmentEnd = std::min( interval.startMD + ( k + 1 ) * length, endMD ); if ( interval.startMD + k * length >= endMD - tolerance ) break; if ( segmentEnd - segmentStart > tolerance ) candidateNodes.push_back( 0.5 * ( segmentStart + segmentEnd ) ); @@ -260,15 +261,16 @@ std::vector RicMswTableDataTools::placeSegmentNodes( double const auto* interval = intervalAtMD( intervals, nodeMD ); if ( interval && interval->maxLength.has_value() ) { - const double maxLength = *interval->maxLength; - const double gap = nodeMD - previousMD; + const double maxLength = *interval->maxLength; + const double splitStart = std::max( previousMD, interval->startMD ); + const double gap = nodeMD - splitStart; if ( gap > maxLength + tolerance ) { const int segmentCount = static_cast( std::ceil( ( gap - tolerance ) / maxLength ) ); const double spacing = gap / segmentCount; for ( int k = 1; k < segmentCount; ++k ) { - nodes.push_back( previousMD + k * spacing ); + nodes.push_back( splitStart + k * spacing ); } } } diff --git a/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.h b/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.h index 3bd1581145..d3725c85a8 100644 --- a/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.h +++ b/ApplicationLibCode/Commands/CompletionExportCommands/RicMswTableDataTools.h @@ -71,15 +71,16 @@ SegmentationSettings segmentationSettings( const RimSegmentCollection* segmentCo //-------------------------------------------------------------------------------------------------- /// Place the segment nodes of a branch. Candidate nodes are the cell (piece) centres in MD order. /// Inside a fixed length interval, the candidates are replaced by nodes at the centre of segments of -/// the fixed length, starting at the interval start and limited to [outletMD, lastMD]. Inside a min -/// length interval, candidates closer than the min length to the previous node are dropped. Inside a -/// max length interval, evenly spaced nodes are inserted until no spacing exceeds the max length. -/// The spacing of the first node is measured from the outlet node. +/// the fixed length, starting at the interval start and limited to [max(outletMD, firstMD), lastMD]. +/// Inside a min length interval, candidates closer than the min length to the previous node are +/// dropped. Inside a max length interval, evenly spaced nodes are inserted until no spacing exceeds +/// the max length; the spacing is measured from the previous node, but never from before the interval start. //-------------------------------------------------------------------------------------------------- std::vector placeSegmentNodes( double outletMD, const std::vector& candidateNodes, const std::vector& intervals, - double lastMD = std::numeric_limits::infinity() ); + double lastMD = std::numeric_limits::infinity(), + double firstMD = -std::numeric_limits::infinity() ); //-------------------------------------------------------------------------------------------------- /// Replace the nodes in [regionStartMD, firstPerforationMD) and/or (lastPerforationMD, regionEndMD] diff --git a/ApplicationLibCode/ProjectDataModel/Completions/RimSegmentInterval.cpp b/ApplicationLibCode/ProjectDataModel/Completions/RimSegmentInterval.cpp index 22b82b7836..d0e49a8d3f 100644 --- a/ApplicationLibCode/ProjectDataModel/Completions/RimSegmentInterval.cpp +++ b/ApplicationLibCode/ProjectDataModel/Completions/RimSegmentInterval.cpp @@ -393,7 +393,6 @@ void RimSegmentInterval::fieldChangedByUi( const caf::PdmFieldHandle* changedFie if ( changedField == &m_fixedSegmentLength || changedField == &m_minSegmentLength || changedField == &m_maxSegmentLength ) { updateUiName(); - uiCapability()->updateConnectedEditors(); } updateConnectedEditors(); @@ -479,6 +478,15 @@ void RimSegmentInterval::enforceSegmentationRules( const caf::PdmFieldHandle* ac else { m_fixedSegmentLength = std::make_pair( false, m_fixedSegmentLength().second ); + + // Min length must not exceed max length, the field just edited wins + if ( m_minSegmentLength().first && m_maxSegmentLength().first && m_minSegmentLength().second > m_maxSegmentLength().second ) + { + if ( activeField == &m_minSegmentLength ) + m_maxSegmentLength = std::make_pair( true, m_minSegmentLength().second ); + else + m_minSegmentLength = std::make_pair( true, m_maxSegmentLength().second ); + } } } diff --git a/ApplicationLibCode/UnitTests/RicMswBranchBuilder-Test.cpp b/ApplicationLibCode/UnitTests/RicMswBranchBuilder-Test.cpp index ace18a6015..597064b852 100644 --- a/ApplicationLibCode/UnitTests/RicMswBranchBuilder-Test.cpp +++ b/ApplicationLibCode/UnitTests/RicMswBranchBuilder-Test.cpp @@ -235,6 +235,20 @@ TEST( RicMswBranchBuilder, PlaceSegmentNodes_FixedLength ) RicMswTableDataTools::placeSegmentNodes( 0.0, centres( exampleCells ), { { 0.0, 50.0, std::nullopt, std::nullopt, 20.0 } }, 45.0 ) ); } +TEST( RicMswBranchBuilder, PlaceSegmentNodes_MaxLengthNotBeforeIntervalStart ) +{ + // Heel at 0, interval [1000,1100], first candidate at 1050: no nodes are added before the interval + const auto nodes = RicMswTableDataTools::placeSegmentNodes( 0.0, { 1050.0 }, { { 1000.0, 1100.0, std::nullopt, 25.0 } } ); + expectNodes( { 1025.0, 1050.0 }, nodes ); +} + +TEST( RicMswBranchBuilder, PlaceSegmentNodes_FixedLengthNotBeforeFirstCell ) +{ + const auto nodes = + RicMswTableDataTools::placeSegmentNodes( 0.0, { 1050.0 }, { { 1000.0, 1100.0, std::nullopt, std::nullopt, 20.0 } }, 1100.0, 1040.0 ); + expectNodes( { 1050.0, 1070.0, 1090.0 }, nodes ); +} + TEST( RicMswBranchBuilder, CollapseNodesOutsidePerforations ) { const std::vector nodes = { 5.0, 12.0, 22.0, 31.5, 46.5, 55.0 }; diff --git a/GrpcInterface/GrpcProtos/SimulatorTables.proto b/GrpcInterface/GrpcProtos/SimulatorTables.proto index 73867270a0..25e0f311c6 100644 --- a/GrpcInterface/GrpcProtos/SimulatorTables.proto +++ b/GrpcInterface/GrpcProtos/SimulatorTables.proto @@ -94,6 +94,7 @@ message SimulatorCompsegsEntry { double distance_start = 5; double distance_end = 6; optional string grid_name = 7; + optional int32 segment_number = 8; } message SimulatorWsegvalvEntry { diff --git a/GrpcInterface/RiaWellPathDataToGrpcConverter.cpp b/GrpcInterface/RiaWellPathDataToGrpcConverter.cpp index 32a69b4fd9..a4c3ef08b9 100644 --- a/GrpcInterface/RiaWellPathDataToGrpcConverter.cpp +++ b/GrpcInterface/RiaWellPathDataToGrpcConverter.cpp @@ -193,6 +193,11 @@ void RiaWellPathDataToGrpcConverter::copyCompsegsToGrpc( const RigMswTableData& { grpcCompData->set_grid_name( compsegsRow.gridName ); } + + if ( compsegsRow.segmentNumber.has_value() ) + { + grpcCompData->set_segment_number( *compsegsRow.segmentNumber ); + } } }