#13316 Custom MSW well segment definitions

This commit is contained in:
Kristian Bendiksen
2025-12-17 16:34:04 +01:00
parent 7768e8c9c9
commit da48c78b02
17 changed files with 1434 additions and 103 deletions
@@ -16,6 +16,8 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RicImportValveTemplatesFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicNewDiameterRoughnessIntervalFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicDeleteDiameterRoughnessIntervalFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicNewCustomSegmentIntervalFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicDeleteCustomSegmentIntervalFeature.h
)
set(SOURCE_GROUP_SOURCE_FILES
@@ -36,6 +38,8 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RicImportValveTemplatesFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicNewDiameterRoughnessIntervalFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicDeleteDiameterRoughnessIntervalFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicNewCustomSegmentIntervalFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicDeleteCustomSegmentIntervalFeature.cpp
)
list(APPEND COMMAND_CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})
@@ -0,0 +1,168 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RicDeleteCustomSegmentIntervalFeature.h"
#include "RimCustomSegmentInterval.h"
#include "RimCustomSegmentIntervalCollection.h"
#include "RimMswCompletionParameters.h"
#include "RimProject.h"
#include "RimWellPath.h"
#include "RimWellPathCompletionSettings.h"
#include "Riu3DMainWindowTools.h"
#include "cafSelectionManager.h"
#include <QAction>
CAF_CMD_SOURCE_INIT( RicDeleteCustomSegmentIntervalFeature, "RicDeleteCustomSegmentIntervalFeature" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicDeleteCustomSegmentIntervalFeature::isCommandEnabled() const
{
if ( caf::SelectionManager::instance()->selectedItemOfType<RimCustomSegmentInterval>( caf::SelectionManager::FIRST_LEVEL ) )
{
return true;
}
if ( auto collection = caf::SelectionManager::instance()->selectedItemOfType<RimCustomSegmentIntervalCollection>() )
{
return collection && !collection->intervals().empty();
}
if ( auto mswParams = caf::SelectionManager::instance()->selectedItemOfType<RimMswCompletionParameters>() )
{
if ( mswParams->customSegmentIntervals() )
{
return !mswParams->customSegmentIntervals()->intervals().empty();
}
}
if ( auto completionSettings = caf::SelectionManager::instance()->selectedItemOfType<RimWellPathCompletionSettings>() )
{
if ( auto wellPath = completionSettings->firstAncestorOrThisOfType<RimWellPath>() )
{
auto mswParams = wellPath->mswCompletionParameters();
if ( mswParams && mswParams->customSegmentIntervals() )
{
return !mswParams->customSegmentIntervals()->intervals().empty();
}
}
}
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicDeleteCustomSegmentIntervalFeature::onActionTriggered( bool isChecked )
{
const auto intervals = caf::SelectionManager::instance()->objectsByType<RimCustomSegmentInterval>( caf::SelectionManager::FIRST_LEVEL );
RimCustomSegmentIntervalCollection* intervalCollection = nullptr;
if ( !intervals.empty() )
{
// Delete selected intervals
intervalCollection = intervals[0]->firstAncestorOrThisOfTypeAsserted<RimCustomSegmentIntervalCollection>();
for ( RimCustomSegmentInterval* intervalToDelete : intervals )
{
intervalCollection->removeInterval( intervalToDelete );
}
intervalCollection->updateConnectedEditors();
}
else
{
// Delete all intervals in collection
intervalCollection = caf::SelectionManager::instance()->selectedItemOfType<RimCustomSegmentIntervalCollection>();
if ( !intervalCollection )
{
// Handle selection of MSW completion parameters
auto mswParams = caf::SelectionManager::instance()->selectedItemOfType<RimMswCompletionParameters>();
if ( mswParams && mswParams->customSegmentIntervals() )
{
intervalCollection = mswParams->customSegmentIntervals();
}
else
{
// Handle selection of well path completion settings
if ( auto completionSettings = caf::SelectionManager::instance()->selectedItemOfType<RimWellPathCompletionSettings>() )
{
if ( auto wellPath = completionSettings->firstAncestorOrThisOfType<RimWellPath>() )
{
auto mswParams = wellPath->mswCompletionParameters();
if ( mswParams && mswParams->customSegmentIntervals() )
{
intervalCollection = mswParams->customSegmentIntervals();
}
}
}
}
}
if ( intervalCollection )
{
intervalCollection->removeAllIntervals();
intervalCollection->updateConnectedEditors();
}
}
if ( intervalCollection )
{
if ( intervalCollection->intervals().empty() )
{
Riu3DMainWindowTools::selectAsCurrentItem( intervalCollection );
}
if ( RimProject* proj = RimProject::current() )
{
proj->scheduleCreateDisplayModelAndRedrawAllViews();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicDeleteCustomSegmentIntervalFeature::setupActionLook( QAction* actionToSetup )
{
const auto intervals = caf::SelectionManager::instance()->objectsByType<RimCustomSegmentInterval>( caf::SelectionManager::FIRST_LEVEL );
if ( !intervals.empty() )
{
if ( intervals.size() == 1 )
{
actionToSetup->setText( "Delete Segment Interval" );
}
else
{
actionToSetup->setText( QString( "Delete %1 Segment Intervals" ).arg( intervals.size() ) );
}
actionToSetup->setIcon( QIcon( ":/Erase.svg" ) );
applyShortcutWithHintToAction( actionToSetup, QKeySequence::Delete );
}
else if ( caf::SelectionManager::instance()->selectedItemOfType<RimCustomSegmentIntervalCollection>() ||
caf::SelectionManager::instance()->selectedItemOfType<RimMswCompletionParameters>() ||
caf::SelectionManager::instance()->selectedItemOfType<RimWellPathCompletionSettings>() )
{
actionToSetup->setText( "Delete All Segment Intervals" );
actionToSetup->setIcon( QIcon( ":/Erase.svg" ) );
applyShortcutWithHintToAction( actionToSetup, QKeySequence::Delete );
}
}
@@ -0,0 +1,34 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "cafCmdFeature.h"
//==================================================================================================
///
//==================================================================================================
class RicDeleteCustomSegmentIntervalFeature : public caf::CmdFeature
{
CAF_CMD_HEADER_INIT;
protected:
bool isCommandEnabled() const override;
void onActionTriggered( bool isChecked ) override;
void setupActionLook( QAction* actionToSetup ) override;
};
@@ -0,0 +1,104 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RicNewCustomSegmentIntervalFeature.h"
#include "RimCustomSegmentInterval.h"
#include "RimCustomSegmentIntervalCollection.h"
#include "RimMswCompletionParameters.h"
#include "RimProject.h"
#include "RimWellPath.h"
#include "RimWellPathCompletionSettings.h"
#include "Riu3DMainWindowTools.h"
#include "cafSelectionManager.h"
#include <QAction>
CAF_CMD_SOURCE_INIT( RicNewCustomSegmentIntervalFeature, "RicNewCustomSegmentIntervalFeature" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicNewCustomSegmentIntervalFeature::isCommandEnabled() const
{
return ( caf::SelectionManager::instance()->selectedItemOfType<RimCustomSegmentIntervalCollection>() ||
caf::SelectionManager::instance()->selectedItemOfType<RimMswCompletionParameters>() ||
caf::SelectionManager::instance()->selectedItemOfType<RimWellPathCompletionSettings>() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicNewCustomSegmentIntervalFeature::onActionTriggered( bool isChecked )
{
// Try to get collection directly
RimCustomSegmentIntervalCollection* intervalCollection =
caf::SelectionManager::instance()->selectedItemOfType<RimCustomSegmentIntervalCollection>();
// If not found, try to get it from MSW completion parameters
if ( !intervalCollection )
{
if ( auto mswParams = caf::SelectionManager::instance()->selectedItemOfType<RimMswCompletionParameters>() )
{
intervalCollection = mswParams->customSegmentIntervals();
}
}
// If still not found, try to get it from well path completion settings
if ( !intervalCollection )
{
if ( auto completionSettings = caf::SelectionManager::instance()->selectedItemOfType<RimWellPathCompletionSettings>() )
{
if ( auto wellPath = completionSettings->firstAncestorOrThisOfType<RimWellPath>() )
{
if ( auto mswParams = wellPath->mswCompletionParameters() )
{
intervalCollection = mswParams->customSegmentIntervals();
}
}
}
}
if ( intervalCollection )
{
RimCustomSegmentInterval* newInterval = new RimCustomSegmentInterval;
// Set default MD values
newInterval->setStartMD( 0 );
newInterval->setEndMD( 100 );
intervalCollection->addInterval( newInterval );
intervalCollection->updateConnectedEditors();
Riu3DMainWindowTools::selectAsCurrentItem( newInterval );
if ( RimProject* project = RimProject::current() )
{
project->scheduleCreateDisplayModelAndRedrawAllViews();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicNewCustomSegmentIntervalFeature::setupActionLook( QAction* actionToSetup )
{
actionToSetup->setText( "New Segment Interval" );
}
@@ -0,0 +1,34 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "cafCmdFeature.h"
//==================================================================================================
///
//==================================================================================================
class RicNewCustomSegmentIntervalFeature : public caf::CmdFeature
{
CAF_CMD_HEADER_INIT;
protected:
bool isCommandEnabled() const override;
void onActionTriggered( bool isChecked ) override;
void setupActionLook( QAction* actionToSetup ) override;
};
@@ -38,10 +38,11 @@
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicMswTableDataTools::collectWelsegsData( RigMswTableData& tableData,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
bool exportCompletionSegmentsAfterMainBore )
void RicMswTableDataTools::collectWelsegsData( RigMswTableData& tableData,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
bool exportCompletionSegmentsAfterMainBore )
{
// Set up WELSEGS header
WelsegsHeader header;
@@ -66,6 +67,7 @@ void RicMswTableDataTools::collectWelsegsData( RigMswTableData& tableData,
exportInfo.mainBoreBranch(),
&segmentNumber,
maxSegmentLength,
customSegmentIntervals,
exportCompletionSegmentsAfterMainBore,
nullptr );
}
@@ -73,13 +75,14 @@ void RicMswTableDataTools::collectWelsegsData( RigMswTableData& tableData,
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicMswTableDataTools::collectWelsegsDataRecursively( RigMswTableData& tableData,
RicMswExportInfo& exportInfo,
gsl::not_null<RicMswBranch*> branch,
gsl::not_null<int*> segmentNumber,
double maxSegmentLength,
bool exportCompletionSegmentsAfterMainBore,
RicMswSegment* connectedToSegment )
void RicMswTableDataTools::collectWelsegsDataRecursively( RigMswTableData& tableData,
RicMswExportInfo& exportInfo,
gsl::not_null<RicMswBranch*> branch,
gsl::not_null<int*> segmentNumber,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
bool exportCompletionSegmentsAfterMainBore,
RicMswSegment* connectedToSegment )
{
auto outletSegment = connectedToSegment;
RicMswValve* outletValve = nullptr;
@@ -90,7 +93,7 @@ void RicMswTableDataTools::collectWelsegsDataRecursively( RigMswTableData&
// Handle tie-in ICV at the beginning of branch
if ( outletValve = dynamic_cast<RicMswTieInICV*>( branch.get() ); outletValve != nullptr )
{
collectValveWelsegsSegment( tableData, outletSegment, outletValve, exportInfo, maxSegmentLength, segmentNumber );
collectValveWelsegsSegment( tableData, outletSegment, outletValve, exportInfo, maxSegmentLength, customSegmentIntervals, segmentNumber );
auto valveSegments = outletValve->segments();
outletSegment = valveSegments.front();
@@ -111,12 +114,19 @@ void RicMswTableDataTools::collectWelsegsDataRecursively( RigMswTableData&
branchDescription = QString( "Segments on branch %1" ).arg( branch->label() );
}
collectWelsegsSegment( tableData, segment, outletSegment, exportInfo, maxSegmentLength, branch, segmentNumber, branchDescription );
collectWelsegsSegment( tableData, segment, outletSegment, exportInfo, maxSegmentLength, customSegmentIntervals, branch, segmentNumber, branchDescription );
outletSegment = segment;
if ( !exportCompletionSegmentsAfterMainBore )
{
collectCompletionsForSegment( tableData, outletSegment, segment, &outletValve, exportInfo, maxSegmentLength, segmentNumber );
collectCompletionsForSegment( tableData,
outletSegment,
segment,
&outletValve,
exportInfo,
maxSegmentLength,
customSegmentIntervals,
segmentNumber );
}
}
@@ -127,7 +137,14 @@ void RicMswTableDataTools::collectWelsegsDataRecursively( RigMswTableData&
for ( ; it != branchSegments.end(); ++it )
{
auto segment = *it;
collectCompletionsForSegment( tableData, outletSegment, segment, &outletValve, exportInfo, maxSegmentLength, segmentNumber );
collectCompletionsForSegment( tableData,
outletSegment,
segment,
&outletValve,
exportInfo,
maxSegmentLength,
customSegmentIntervals,
segmentNumber );
}
}
@@ -143,6 +160,7 @@ void RicMswTableDataTools::collectWelsegsDataRecursively( RigMswTableData&
childBranch,
segmentNumber,
maxSegmentLength,
customSegmentIntervals,
exportCompletionSegmentsAfterMainBore,
outletSegmentForChildBranch );
}
@@ -151,21 +169,23 @@ void RicMswTableDataTools::collectWelsegsDataRecursively( RigMswTableData&
//--------------------------------------------------------------------------------------------------
/// Helper function to collect WELSEGS data for a single segment with sub-segmentation
//--------------------------------------------------------------------------------------------------
void RicMswTableDataTools::collectWelsegsSegment( RigMswTableData& tableData,
RicMswSegment* segment,
const RicMswSegment* previousSegment,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
gsl::not_null<RicMswBranch*> branch,
int* segmentNumber,
QString branchDescription )
void RicMswTableDataTools::collectWelsegsSegment( RigMswTableData& tableData,
RicMswSegment* segment,
const RicMswSegment* previousSegment,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
gsl::not_null<RicMswBranch*> branch,
int* segmentNumber,
QString branchDescription )
{
CVF_ASSERT( segment && segmentNumber );
double startMD = segment->startMD();
double endMD = segment->endMD();
std::vector<std::pair<double, double>> segments = RicMswTableFormatterTools::createSubSegmentMDPairs( startMD, endMD, maxSegmentLength );
std::vector<std::pair<double, double>> segments =
RicMswTableFormatterTools::createSubSegmentMDPairs( startMD, endMD, maxSegmentLength, customSegmentIntervals );
CVF_ASSERT( branch->wellPath() );
@@ -252,12 +272,13 @@ void RicMswTableDataTools::collectWelsegsSegment( RigMswTableData& t
/// Helper function to collect WELSEGS data for valve completions
/// Ported from RicMswTableFormatterTools::writeValveWelsegsSegment()
//--------------------------------------------------------------------------------------------------
void RicMswTableDataTools::collectValveWelsegsSegment( RigMswTableData& tableData,
const RicMswSegment* outletSegment,
RicMswValve* valve,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
int* segmentNumber )
void RicMswTableDataTools::collectValveWelsegsSegment( RigMswTableData& tableData,
const RicMswSegment* outletSegment,
RicMswValve* valve,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
int* segmentNumber )
{
if ( !valve ) return;
if ( !valve->isValid() ) return;
@@ -290,7 +311,7 @@ void RicMswTableDataTools::collectValveWelsegsSegment( RigMswTableData& tabl
endMD = subSegment->endMD();
}
auto splitSegments = RicMswTableFormatterTools::createSubSegmentMDPairs( startMD, endMD, maxSegmentLength );
auto splitSegments = RicMswTableFormatterTools::createSubSegmentMDPairs( startMD, endMD, maxSegmentLength, customSegmentIntervals );
int outletSegmentNumber = outletSegment ? outletSegment->segmentNumber() : 1;
const auto linerDiameter = valve->wellPath()->mswCompletionParameters()->linerDiameter( exportInfo.unitSystem() );
@@ -344,13 +365,14 @@ void RicMswTableDataTools::collectValveWelsegsSegment( RigMswTableData& tabl
//--------------------------------------------------------------------------------------------------
/// Helper function to collect WELSEGS data for completions on a segment
//--------------------------------------------------------------------------------------------------
void RicMswTableDataTools::collectCompletionsForSegment( RigMswTableData& tableData,
gsl::not_null<const RicMswSegment*> outletSegment,
gsl::not_null<RicMswSegment*> segment,
RicMswValve** outletValve,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
int* segmentNumber )
void RicMswTableDataTools::collectCompletionsForSegment( RigMswTableData& tableData,
gsl::not_null<const RicMswSegment*> outletSegment,
gsl::not_null<RicMswSegment*> segment,
RicMswValve** outletValve,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
int* segmentNumber )
{
for ( auto& completion : segment->completions() )
{
@@ -363,7 +385,7 @@ void RicMswTableDataTools::collectCompletionsForSegment( RigMswTableData&
auto fishboneIcd = dynamic_cast<RicMswFishbonesICD*>( completion );
if ( !fishboneIcd && segmentValve != nullptr )
{
collectValveWelsegsSegment( tableData, segment, segmentValve, exportInfo, maxSegmentLength, segmentNumber );
collectValveWelsegsSegment( tableData, segment, segmentValve, exportInfo, maxSegmentLength, customSegmentIntervals, segmentNumber );
*outletValve = segmentValve;
}
else if ( dynamic_cast<RicMswTieInICV*>( completion ) )
@@ -371,12 +393,18 @@ void RicMswTableDataTools::collectCompletionsForSegment( RigMswTableData&
// Special handling for Tie-in ICVs
RicMswSegment* outletSegmentForCompletion =
*outletValve && ( *outletValve )->segmentCount() > 0 ? ( *outletValve )->segments().front() : segment.get();
collectCompletionWelsegsSegments( tableData, outletSegmentForCompletion, completion, exportInfo, maxSegmentLength, segmentNumber );
collectCompletionWelsegsSegments( tableData,
outletSegmentForCompletion,
completion,
exportInfo,
maxSegmentLength,
customSegmentIntervals,
segmentNumber );
}
else
{
// This is the default case for completions that are not valves
collectCompletionWelsegsSegments( tableData, segment, completion, exportInfo, maxSegmentLength, segmentNumber );
collectCompletionWelsegsSegments( tableData, segment, completion, exportInfo, maxSegmentLength, customSegmentIntervals, segmentNumber );
}
}
}
@@ -385,12 +413,13 @@ void RicMswTableDataTools::collectCompletionsForSegment( RigMswTableData&
/// Helper function to collect WELSEGS data for a completion's segments
/// Porting of RicMswTableFormatterTools::writeCompletionWelsegsSegments()
//--------------------------------------------------------------------------------------------------
void RicMswTableDataTools::collectCompletionWelsegsSegments( RigMswTableData& tableData,
gsl::not_null<const RicMswSegment*> outletSegment,
gsl::not_null<RicMswCompletion*> completion,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
int* segmentNumber )
void RicMswTableDataTools::collectCompletionWelsegsSegments( RigMswTableData& tableData,
gsl::not_null<const RicMswSegment*> outletSegment,
gsl::not_null<RicMswCompletion*> completion,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
int* segmentNumber )
{
bool isDescriptionAdded = false;
@@ -405,7 +434,7 @@ void RicMswTableDataTools::collectCompletionWelsegsSegments( RigMswTableData&
double startTVD = segment->startTVD();
double endTVD = segment->endTVD();
auto splitSegments = RicMswTableFormatterTools::createSubSegmentMDPairs( startMD, endMD, maxSegmentLength );
auto splitSegments = RicMswTableFormatterTools::createSubSegmentMDPairs( startMD, endMD, maxSegmentLength, customSegmentIntervals );
for ( const auto& [subStartMD, subEndMD] : splitSegments )
{
int subSegmentNumber = ( *segmentNumber )++;
@@ -469,7 +498,7 @@ void RicMswTableDataTools::collectCompletionWelsegsSegments( RigMswTableData&
for ( auto comp : segment->completions() )
{
collectCompletionWelsegsSegments( tableData, segment, comp, exportInfo, maxSegmentLength, segmentNumber );
collectCompletionWelsegsSegments( tableData, segment, comp, exportInfo, maxSegmentLength, customSegmentIntervals, segmentNumber );
}
}
}
@@ -74,18 +74,20 @@ public:
};
// New data collection functions (replace formatter versions)
void collectWelsegsData( RigMswTableData& tableData,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
bool exportCompletionSegmentsAfterMainBore );
void collectWelsegsData( RigMswTableData& tableData,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
bool exportCompletionSegmentsAfterMainBore );
void collectWelsegsDataRecursively( RigMswTableData& tableData,
RicMswExportInfo& exportInfo,
gsl::not_null<RicMswBranch*> branch,
gsl::not_null<int*> segmentNumber,
double maxSegmentLength,
bool exportCompletionSegmentsAfterMainBore,
RicMswSegment* connectedToSegment );
void collectWelsegsDataRecursively( RigMswTableData& tableData,
RicMswExportInfo& exportInfo,
gsl::not_null<RicMswBranch*> branch,
gsl::not_null<int*> segmentNumber,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
bool exportCompletionSegmentsAfterMainBore,
RicMswSegment* connectedToSegment );
void collectCompsegData( RigMswTableData& tableData, RicMswExportInfo& exportInfo, bool exportSubGridIntersections );
@@ -105,36 +107,40 @@ void collectWsegAicdData( RigMswTableData& tableData, RicMswExportInfo& exportIn
void collectWsegAicdDataRecursively( RigMswTableData& tableData, RicMswExportInfo& exportInfo, gsl::not_null<const RicMswBranch*> branch );
// Helper functions for data collection
void collectWelsegsSegment( RigMswTableData& tableData,
RicMswSegment* segment,
const RicMswSegment* previousSegment,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
gsl::not_null<RicMswBranch*> branch,
int* segmentNumber,
QString branchDescription );
void collectWelsegsSegment( RigMswTableData& tableData,
RicMswSegment* segment,
const RicMswSegment* previousSegment,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
gsl::not_null<RicMswBranch*> branch,
int* segmentNumber,
QString branchDescription );
void collectValveWelsegsSegment( RigMswTableData& tableData,
const RicMswSegment* outletSegment,
RicMswValve* valve,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
int* segmentNumber );
void collectValveWelsegsSegment( RigMswTableData& tableData,
const RicMswSegment* outletSegment,
RicMswValve* valve,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
int* segmentNumber );
void collectCompletionsForSegment( RigMswTableData& tableData,
gsl::not_null<const RicMswSegment*> outletSegment,
gsl::not_null<RicMswSegment*> segment,
RicMswValve** outletValve,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
int* segmentNumber );
void collectCompletionsForSegment( RigMswTableData& tableData,
gsl::not_null<const RicMswSegment*> outletSegment,
gsl::not_null<RicMswSegment*> segment,
RicMswValve** outletValve,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
int* segmentNumber );
void collectCompletionWelsegsSegments( RigMswTableData& tableData,
gsl::not_null<const RicMswSegment*> outletSegment,
gsl::not_null<RicMswCompletion*> completion,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
int* segmentNumber );
void collectCompletionWelsegsSegments( RigMswTableData& tableData,
gsl::not_null<const RicMswSegment*> outletSegment,
gsl::not_null<RicMswCompletion*> completion,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
int* segmentNumber );
void generateWsegAicdTableRecursively( RicMswExportInfo& exportInfo,
gsl::not_null<const RicMswBranch*> branch,
@@ -910,24 +910,109 @@ void RicMswTableFormatterTools::writeCompletionsForSegment( gsl::not_null<const
}
//--------------------------------------------------------------------------------------------------
///
/// Creates sub-segment MD pairs by combining custom intervals with max segment length subdivision
/// Custom intervals define exact segment boundaries where specified
/// Areas without custom intervals use max segment length subdivision (if maxSegmentLength > 0)
//--------------------------------------------------------------------------------------------------
std::vector<std::pair<double, double>> RicMswTableFormatterTools::createSubSegmentMDPairs( double startMD, double endMD, double maxSegmentLength )
std::vector<std::pair<double, double>>
RicMswTableFormatterTools::createSubSegmentMDPairs( double startMD,
double endMD,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals )
{
int subSegmentCount = (int)( std::trunc( ( endMD - startMD ) / maxSegmentLength ) + 1 );
double subSegmentLength = ( endMD - startMD ) / subSegmentCount;
std::vector<std::pair<double, double>> subSegmentMDPairs;
double subStartMD = startMD;
double subEndMD = startMD + subSegmentLength;
for ( int i = 0; i < subSegmentCount; ++i )
// If no custom intervals, use original logic with maxSegmentLength subdivision
if ( customSegmentIntervals.empty() || maxSegmentLength <= 0.0 )
{
subSegmentMDPairs.push_back( std::make_pair( subStartMD, subEndMD ) );
subStartMD += subSegmentLength;
subEndMD += std::min( subSegmentLength, endMD );
int subSegmentCount = maxSegmentLength > 0.0 ? (int)( std::trunc( ( endMD - startMD ) / maxSegmentLength ) + 1 ) : 1;
double subSegmentLength = ( endMD - startMD ) / subSegmentCount;
double subStartMD = startMD;
double subEndMD = startMD + subSegmentLength;
for ( int i = 0; i < subSegmentCount; ++i )
{
subSegmentMDPairs.push_back( std::make_pair( subStartMD, subEndMD ) );
subStartMD += subSegmentLength;
subEndMD = std::min( subEndMD + subSegmentLength, endMD );
}
return subSegmentMDPairs;
}
// Combine custom intervals with maxSegmentLength subdivision
// Collect all boundaries (start, end, custom interval boundaries) and sort them
std::set<double> boundaries;
boundaries.insert( startMD );
boundaries.insert( endMD );
// Add custom interval boundaries that overlap with [startMD, endMD]
for ( const auto& [customStart, customEnd] : customSegmentIntervals )
{
// Check if custom interval overlaps with [startMD, endMD]
if ( customEnd > startMD && customStart < endMD )
{
// Clip custom interval to [startMD, endMD] range
double clippedStart = std::max( customStart, startMD );
double clippedEnd = std::min( customEnd, endMD );
if ( clippedStart < clippedEnd )
{
boundaries.insert( clippedStart );
boundaries.insert( clippedEnd );
}
}
}
// Convert boundaries to sorted vector
std::vector<double> sortedBoundaries( boundaries.begin(), boundaries.end() );
// For each gap between boundaries, either:
// - Use exact boundary if it's from a custom interval
// - Subdivide using maxSegmentLength if it's a gap
for ( size_t i = 0; i + 1 < sortedBoundaries.size(); ++i )
{
double gapStart = sortedBoundaries[i];
double gapEnd = sortedBoundaries[i + 1];
// Check if this gap is covered by a custom interval
bool coveredByCustomInterval = false;
for ( const auto& [customStart, customEnd] : customSegmentIntervals )
{
double clippedStart = std::max( customStart, startMD );
double clippedEnd = std::min( customEnd, endMD );
// If the gap is fully within a custom interval, use exact boundaries
if ( gapStart >= clippedStart && gapEnd <= clippedEnd && std::abs( gapStart - clippedStart ) < 1e-6 &&
std::abs( gapEnd - clippedEnd ) < 1e-6 )
{
coveredByCustomInterval = true;
subSegmentMDPairs.push_back( std::make_pair( gapStart, gapEnd ) );
break;
}
}
// If not covered by custom interval, subdivide using maxSegmentLength
if ( !coveredByCustomInterval && maxSegmentLength > 0.0 )
{
double gapLength = gapEnd - gapStart;
int subCount = (int)( std::trunc( gapLength / maxSegmentLength ) + 1 );
double subLength = gapLength / subCount;
double subStart = gapStart;
for ( int j = 0; j < subCount; ++j )
{
double subEnd = ( j == subCount - 1 ) ? gapEnd : subStart + subLength;
subSegmentMDPairs.push_back( std::make_pair( subStart, subEnd ) );
subStart = subEnd;
}
}
else if ( !coveredByCustomInterval )
{
// No maxSegmentLength, use gap as-is
subSegmentMDPairs.push_back( std::make_pair( gapStart, gapEnd ) );
}
}
return subSegmentMDPairs;
}
@@ -122,7 +122,10 @@ void generateWsegvalvTableRecursively( gsl::not_null<RicMswBranch*>
void generateWsegAicdTable( RifTextDataTableFormatter& formatter, RicMswExportInfo& exportInfo );
std::vector<std::pair<double, double>> createSubSegmentMDPairs( double startMD, double endMD, double maxSegmentLength );
std::vector<std::pair<double, double>> createSubSegmentMDPairs( double startMD,
double endMD,
double maxSegmentLength,
const std::vector<std::pair<double, double>>& customSegmentIntervals = {} );
double tvdFromMeasuredDepth( gsl::not_null<const RimWellPath*> wellPath, double measuredDepth );
@@ -119,8 +119,15 @@ std::expected<RigMswTableData, std::string> RicWellPathExportMswTableData::extra
// Create table data container and extract data
RigMswTableData tableData( wellPath->completionSettings()->wellNameForExport().toStdString(), unitSystem );
// Extract custom segment intervals from MSW parameters
std::vector<std::pair<double, double>> customSegmentIntervals = mswParameters->getSegmentIntervals();
// Use the new collection functions to populate the table data
RicMswTableDataTools::collectWelsegsData( tableData, exportInfo, mswParameters->maxSegmentLength(), exportCompletionsAfterMainBoreSegments );
RicMswTableDataTools::collectWelsegsData( tableData,
exportInfo,
mswParameters->maxSegmentLength(),
customSegmentIntervals,
exportCompletionsAfterMainBoreSegments );
{
// Get COMPSEGS for main grid
@@ -34,6 +34,8 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimWellPathAicdParameters.h
${CMAKE_CURRENT_LIST_DIR}/RimDiameterRoughnessInterval.h
${CMAKE_CURRENT_LIST_DIR}/RimDiameterRoughnessIntervalCollection.h
${CMAKE_CURRENT_LIST_DIR}/RimCustomSegmentInterval.h
${CMAKE_CURRENT_LIST_DIR}/RimCustomSegmentIntervalCollection.h
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleFractureStatistics.h
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleFractureStatisticsCollection.h
)
@@ -71,6 +73,8 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimWellPathAicdParameters.cpp
${CMAKE_CURRENT_LIST_DIR}/RimDiameterRoughnessInterval.cpp
${CMAKE_CURRENT_LIST_DIR}/RimDiameterRoughnessIntervalCollection.cpp
${CMAKE_CURRENT_LIST_DIR}/RimCustomSegmentInterval.cpp
${CMAKE_CURRENT_LIST_DIR}/RimCustomSegmentIntervalCollection.cpp
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleFractureStatistics.cpp
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleFractureStatisticsCollection.cpp
)
@@ -0,0 +1,240 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimCustomSegmentInterval.h"
#include "RiaApplication.h"
#include "RiaLogging.h"
#include "RimCustomSegmentIntervalCollection.h"
#include "cafPdmFieldScriptingCapability.h"
#include "cafPdmObjectScriptingCapability.h"
#include <cmath>
CAF_PDM_SOURCE_INIT( RimCustomSegmentInterval, "CustomSegmentInterval" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCustomSegmentInterval::RimCustomSegmentInterval()
{
CAF_PDM_InitScriptableObject( "Custom Segment Interval", ":/WellPathComponent16x16.png", "", "CustomSegmentInterval" );
CAF_PDM_InitScriptableField( &m_startMD, "StartMd", 0.0, "Start MD" );
CAF_PDM_InitScriptableField( &m_endMD, "EndMd", 100.0, "End MD" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCustomSegmentInterval::~RimCustomSegmentInterval()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimCustomSegmentInterval::startMD() const
{
return m_startMD;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimCustomSegmentInterval::endMD() const
{
return m_endMD;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentInterval::setStartMD( double startMD )
{
m_startMD = startMD;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentInterval::setEndMD( double endMD )
{
m_endMD = endMD;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimCustomSegmentInterval::isValidInterval() const
{
return m_endMD > m_startMD;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimCustomSegmentInterval::overlaps( const RimCustomSegmentInterval* other ) const
{
if ( !other ) return false;
return m_endMD > other->startMD() && m_startMD < other->endMD();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimCustomSegmentInterval::containsMD( double md ) const
{
return md >= m_startMD && md <= m_endMD;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimCustomSegmentInterval::operator<( const RimCustomSegmentInterval& rhs ) const
{
return m_startMD < rhs.m_startMD;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimCustomSegmentInterval::isEnabled() const
{
return true; // Always enabled
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaDefines::WellPathComponentType RimCustomSegmentInterval::componentType() const
{
return RiaDefines::WellPathComponentType::PERFORATION_INTERVAL;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimCustomSegmentInterval::componentLabel() const
{
return generateDisplayLabel();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimCustomSegmentInterval::componentTypeLabel() const
{
return "Custom Segment";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::Color3f RimCustomSegmentInterval::defaultComponentColor() const
{
return cvf::Color3f( 0.2f, 0.6f, 0.8f ); // Blue color for custom segments
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentInterval::applyOffset( double offsetMD )
{
m_startMD = m_startMD + offsetMD;
m_endMD = m_endMD + offsetMD;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentInterval::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
if ( changedField == &m_startMD || changedField == &m_endMD )
{
// Validate interval
if ( m_startMD >= m_endMD )
{
RiaLogging::warning( "Invalid interval: Start MD must be less than End MD" );
}
// Update overlap visual feedback in parent collection
auto* collection = firstAncestorOrThisOfType<RimCustomSegmentIntervalCollection>();
if ( collection )
{
collection->updateOverlapVisualFeedback();
}
}
updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentInterval::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
uiOrdering.add( &m_startMD );
uiOrdering.add( &m_endMD );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentInterval::updateConnectedEditors()
{
// Update any connected UI editors
m_startMD.uiCapability()->updateConnectedEditors();
m_endMD.uiCapability()->updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimCustomSegmentInterval::generateDisplayLabel() const
{
return QString( "MD %.1f-%.1f" ).arg( m_startMD() ).arg( m_endMD() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentInterval::updateOverlapVisualFeedback( bool hasOverlap )
{
if ( hasOverlap )
{
// Set red color for overlapping fields
m_startMD.uiCapability()->setUiContentTextColor( Qt::red );
m_endMD.uiCapability()->setUiContentTextColor( Qt::red );
// Create tooltip with overlap information
QString tooltip = "This interval overlaps with another interval!";
m_startMD.uiCapability()->setUiToolTip( tooltip );
m_endMD.uiCapability()->setUiToolTip( tooltip );
}
else
{
// Reset color and tooltip
m_startMD.uiCapability()->setUiContentTextColor( QColor() );
m_endMD.uiCapability()->setUiContentTextColor( QColor() );
m_startMD.uiCapability()->setUiToolTip( "" );
m_endMD.uiCapability()->setUiToolTip( "" );
}
}
@@ -0,0 +1,81 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "RiaDefines.h"
#include "RimWellPathComponentInterface.h"
#include "cafPdmField.h"
#include "cafPdmObject.h"
#include <QString>
class RimWellPath;
//==================================================================================================
///
/// Represents a custom segment interval for a specific measured depth range
///
//==================================================================================================
class RimCustomSegmentInterval : public caf::PdmObject, public RimWellPathComponentInterface
{
CAF_PDM_HEADER_INIT;
public:
RimCustomSegmentInterval();
~RimCustomSegmentInterval() override;
// Getters
double startMD() const override;
double endMD() const override;
// Setters
void setStartMD( double startMD );
void setEndMD( double endMD );
// Validation
bool isValidInterval() const;
bool overlaps( const RimCustomSegmentInterval* other ) const;
bool containsMD( double md ) const;
// Comparison for sorting
bool operator<( const RimCustomSegmentInterval& rhs ) const;
// Overrides from RimWellPathComponentInterface
bool isEnabled() const override;
RiaDefines::WellPathComponentType componentType() const override;
QString componentLabel() const override;
QString componentTypeLabel() const override;
cvf::Color3f defaultComponentColor() const override;
void applyOffset( double offsetMD ) override;
// Visual feedback for overlapping intervals
void updateOverlapVisualFeedback( bool hasOverlap );
protected:
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
private:
void updateConnectedEditors();
QString generateDisplayLabel() const;
private:
caf::PdmField<double> m_startMD;
caf::PdmField<double> m_endMD;
};
@@ -0,0 +1,378 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimCustomSegmentIntervalCollection.h"
#include "RiaLogging.h"
#include "RimCustomSegmentInterval.h"
#include "cafCmdFeatureMenuBuilder.h"
#include "cafPdmUiTableViewEditor.h"
#include <algorithm>
#include <cmath>
CAF_PDM_SOURCE_INIT( RimCustomSegmentIntervalCollection, "CustomSegmentIntervalCollection" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCustomSegmentIntervalCollection::RimCustomSegmentIntervalCollection()
{
CAF_PDM_InitObject( "Custom Segment Intervals", ":/WellPathComponent16x16.png" );
CAF_PDM_InitFieldNoDefault( &m_intervals, "Intervals", "Intervals" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCustomSegmentIntervalCollection::~RimCustomSegmentIntervalCollection()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimCustomSegmentInterval*> RimCustomSegmentIntervalCollection::intervals() const
{
return m_intervals.childrenByType();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentIntervalCollection::addInterval( RimCustomSegmentInterval* interval )
{
if ( interval )
{
m_intervals.push_back( interval );
sortIntervalsByMD();
updateConnectedEditors();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentIntervalCollection::removeInterval( RimCustomSegmentInterval* interval )
{
if ( interval )
{
m_intervals.removeChild( interval );
delete interval;
updateConnectedEditors();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentIntervalCollection::removeAllIntervals()
{
m_intervals.deleteChildren();
updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCustomSegmentInterval* RimCustomSegmentIntervalCollection::createInterval( double startMD, double endMD )
{
auto* interval = new RimCustomSegmentInterval();
interval->setStartMD( startMD );
interval->setEndMD( endMD );
addInterval( interval );
return interval;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCustomSegmentInterval* RimCustomSegmentIntervalCollection::createDefaultInterval()
{
auto* interval = new RimCustomSegmentInterval();
addInterval( interval );
return interval;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCustomSegmentInterval* RimCustomSegmentIntervalCollection::findIntervalAtMD( double md ) const
{
for ( auto* interval : intervals() )
{
if ( interval && interval->containsMD( md ) )
{
return interval;
}
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimCustomSegmentIntervalCollection::hasValidIntervals() const
{
if ( isEmpty() ) return false;
for ( auto* interval : intervals() )
{
if ( !interval || !interval->isValidInterval() )
{
return false;
}
}
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimCustomSegmentIntervalCollection::hasOverlappingIntervals() const
{
auto intervalList = intervals();
for ( size_t i = 0; i < intervalList.size(); ++i )
{
for ( size_t j = i + 1; j < intervalList.size(); ++j )
{
if ( intervalList[i]->overlaps( intervalList[j] ) )
{
return true;
}
}
}
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QString> RimCustomSegmentIntervalCollection::validateIntervals() const
{
std::vector<QString> issues;
if ( isEmpty() )
{
issues.push_back( "No intervals defined" );
return issues;
}
auto intervalList = intervals();
// Check for invalid intervals
for ( auto* interval : intervalList )
{
if ( !interval->isValidInterval() )
{
issues.push_back(
QString( "Invalid interval: MD %.1f-%.1f (Start MD must be less than End MD)" ).arg( interval->startMD() ).arg( interval->endMD() ) );
}
}
// Check for overlaps
for ( size_t i = 0; i < intervalList.size(); ++i )
{
for ( size_t j = i + 1; j < intervalList.size(); ++j )
{
if ( intervalList[i]->overlaps( intervalList[j] ) )
{
issues.push_back( QString( "Overlapping intervals: MD %.1f-%.1f and MD %.1f-%.1f" )
.arg( intervalList[i]->startMD() )
.arg( intervalList[i]->endMD() )
.arg( intervalList[j]->startMD() )
.arg( intervalList[j]->endMD() ) );
}
}
}
return issues;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentIntervalCollection::sortIntervalsByMD()
{
auto intervalList = intervals();
std::sort( intervalList.begin(),
intervalList.end(),
[]( const RimCustomSegmentInterval* a, const RimCustomSegmentInterval* b ) { return *a < *b; } );
// Rebuild the collection in sorted order
m_intervals.clearWithoutDelete();
for ( auto* interval : intervalList )
{
m_intervals.push_back( interval );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimCustomSegmentIntervalCollection::isEmpty() const
{
return m_intervals.empty();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RimCustomSegmentIntervalCollection::count() const
{
return m_intervals.size();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentIntervalCollection::updateConnectedEditors()
{
m_intervals.uiCapability()->updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::PdmChildArrayField<RimCustomSegmentInterval*>& RimCustomSegmentIntervalCollection::intervalsField()
{
return m_intervals;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentIntervalCollection::updateOverlapVisualFeedback()
{
auto intervalList = intervals();
// First, reset all intervals to no overlap
for ( auto* interval : intervalList )
{
interval->updateOverlapVisualFeedback( false );
}
// Then check for overlaps and update visual feedback
for ( size_t i = 0; i < intervalList.size(); ++i )
{
bool hasOverlap = false;
for ( size_t j = 0; j < intervalList.size(); ++j )
{
if ( i != j && intervalList[i]->overlaps( intervalList[j] ) )
{
hasOverlap = true;
break;
}
}
if ( hasOverlap )
{
intervalList[i]->updateOverlapVisualFeedback( true );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentIntervalCollection::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
const QVariant& oldValue,
const QVariant& newValue )
{
if ( changedField == &m_intervals )
{
sortIntervalsByMD();
updateOverlapVisualFeedback();
}
updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentIntervalCollection::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
uiOrdering.add( &m_intervals );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentIntervalCollection::defineEditorAttribute( const caf::PdmFieldHandle* field,
QString uiConfigName,
caf::PdmUiEditorAttribute* attribute )
{
if ( field == &m_intervals )
{
auto tvAttribute = dynamic_cast<caf::PdmUiTableViewEditorAttribute*>( attribute );
if ( tvAttribute )
{
tvAttribute->resizePolicy = caf::PdmUiTableViewEditorAttribute::RESIZE_TO_FILL_CONTAINER;
tvAttribute->alwaysEnforceResizePolicy = true;
tvAttribute->minimumHeight = 300;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentIntervalCollection::onChildDeleted( caf::PdmChildArrayFieldHandle* childArray,
std::vector<caf::PdmObjectHandle*>& referringObjects )
{
updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentIntervalCollection::defineCustomContextMenu( const caf::PdmFieldHandle* fieldNeedingMenu,
QMenu* menu,
QWidget* fieldEditorWidget )
{
caf::CmdFeatureMenuBuilder menuBuilder;
menuBuilder << "RicNewCustomSegmentIntervalFeature";
menuBuilder << "Separator";
menuBuilder << "RicDeleteCustomSegmentIntervalFeature";
menuBuilder.appendToMenu( menu );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimCustomSegmentIntervalCollection::insertInterval( RimCustomSegmentInterval* insertBefore, RimCustomSegmentInterval* interval )
{
if ( !interval ) return;
size_t index = m_intervals.indexOf( insertBefore );
if ( index < m_intervals.size() )
m_intervals.insert( index, interval );
else
m_intervals.push_back( interval );
sortIntervalsByMD();
updateConnectedEditors();
}
@@ -0,0 +1,85 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "RiaDefines.h"
#include "cafPdmChildArrayField.h"
#include "cafPdmObject.h"
#include <vector>
class RimCustomSegmentInterval;
//==================================================================================================
///
/// Collection to manage custom segment intervals for measured depth ranges
///
//==================================================================================================
class RimCustomSegmentIntervalCollection : public caf::PdmObject
{
CAF_PDM_HEADER_INIT;
public:
RimCustomSegmentIntervalCollection();
~RimCustomSegmentIntervalCollection() override;
// Collection management
std::vector<RimCustomSegmentInterval*> intervals() const;
void addInterval( RimCustomSegmentInterval* interval );
void insertInterval( RimCustomSegmentInterval* insertBefore, RimCustomSegmentInterval* interval );
void removeInterval( RimCustomSegmentInterval* interval );
void removeAllIntervals();
// Interval creation
RimCustomSegmentInterval* createInterval( double startMD, double endMD );
RimCustomSegmentInterval* createDefaultInterval();
// Lookup methods
RimCustomSegmentInterval* findIntervalAtMD( double md ) const;
// Validation
bool hasValidIntervals() const;
bool hasOverlappingIntervals() const;
std::vector<QString> validateIntervals() const;
// Sorting and organization
void sortIntervalsByMD();
// Utility
bool isEmpty() const;
size_t count() const;
void updateConnectedEditors();
// Accessor for UI integration
caf::PdmChildArrayField<RimCustomSegmentInterval*>& intervalsField();
// Visual feedback for overlapping intervals
void updateOverlapVisualFeedback();
protected:
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
void defineCustomContextMenu( const caf::PdmFieldHandle* fieldNeedingMenu, QMenu* menu, QWidget* fieldEditorWidget ) override;
void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
private:
void onChildDeleted( caf::PdmChildArrayFieldHandle* childArray, std::vector<caf::PdmObjectHandle*>& referringObjects ) override;
caf::PdmChildArrayField<RimCustomSegmentInterval*> m_intervals;
};
@@ -19,6 +19,8 @@
#include "RimMswCompletionParameters.h"
#include "RiaEclipseUnitTools.h"
#include "RimCustomSegmentInterval.h"
#include "RimCustomSegmentIntervalCollection.h"
#include "RimDiameterRoughnessIntervalCollection.h"
#include "RimWellPath.h"
@@ -113,6 +115,12 @@ RimMswCompletionParameters::RimMswCompletionParameters()
CAF_PDM_InitScriptableField( &m_enforceMaxSegmentLength, "EnforceMaxSegmentLength", false, "Enforce Max Segment Length" );
CAF_PDM_InitScriptableField( &m_maxSegmentLength, "MaxSegmentLength", 200.0, "Max Segment Length" );
m_maxSegmentLength.uiCapability()->setUiHidden( true );
CAF_PDM_InitFieldNoDefault( &m_customSegmentIntervals, "CustomSegmentIntervals", "Custom Segment Intervals" );
m_customSegmentIntervals = new RimCustomSegmentIntervalCollection();
m_customSegmentIntervals->intervalsField().uiCapability()->setUiEditorTypeName( caf::PdmUiTableViewEditor::uiEditorTypeName() );
m_customSegmentIntervals->intervalsField().uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::TOP );
m_customSegmentIntervals->intervalsField().uiCapability()->setCustomContextMenuEnabled( true );
}
//--------------------------------------------------------------------------------------------------
@@ -380,6 +388,44 @@ void RimMswCompletionParameters::setLengthAndDepth( LengthAndDepthType lengthAnd
m_lengthAndDepth = lengthAndDepthType;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCustomSegmentIntervalCollection* RimMswCompletionParameters::customSegmentIntervals() const
{
return m_customSegmentIntervals();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<std::pair<double, double>> RimMswCompletionParameters::getSegmentIntervals() const
{
std::vector<std::pair<double, double>> result;
if ( m_customSegmentIntervals() && !m_customSegmentIntervals()->isEmpty() )
{
auto intervals = m_customSegmentIntervals()->intervals();
for ( auto* interval : intervals )
{
if ( interval && interval->isValidInterval() )
{
result.push_back( { interval->startMD(), interval->endMD() } );
}
}
}
return result;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimMswCompletionParameters::hasCustomSegmentIntervals() const
{
return m_customSegmentIntervals() && !m_customSegmentIntervals()->isEmpty();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -450,8 +496,12 @@ void RimMswCompletionParameters::defineUiOrdering( QString uiConfigName, caf::Pd
uiOrdering.add( &m_pressureDrop );
uiOrdering.add( &m_lengthAndDepth );
uiOrdering.add( &m_enforceMaxSegmentLength );
uiOrdering.add( &m_maxSegmentLength );
// Custom Segment Configuration section
auto* segmentConfigGroup = uiOrdering.addNewGroup( "Custom Segment Configuration" );
segmentConfigGroup->add( &m_enforceMaxSegmentLength );
segmentConfigGroup->add( &m_maxSegmentLength );
segmentConfigGroup->add( &m_customSegmentIntervals->intervalsField() );
}
else
{
@@ -545,4 +595,15 @@ void RimMswCompletionParameters::defineCustomContextMenu( const caf::PdmFieldHan
menuBuilder.appendToMenu( menu );
}
if ( fieldNeedingMenu == &m_customSegmentIntervals )
{
caf::CmdFeatureMenuBuilder menuBuilder;
menuBuilder << "RicNewCustomSegmentIntervalFeature";
menuBuilder << "Separator";
menuBuilder << "RicDeleteCustomSegmentIntervalFeature";
menuBuilder.appendToMenu( menu );
}
}
@@ -25,6 +25,7 @@
#include "cafPdmUiGroup.h"
class RimDiameterRoughnessIntervalCollection;
class RimCustomSegmentIntervalCollection;
class RimMswCompletionParameters : public caf::PdmObject
{
@@ -93,6 +94,11 @@ public:
RimDiameterRoughnessIntervalCollection* diameterRoughnessIntervals() const;
// Custom segment intervals
RimCustomSegmentIntervalCollection* customSegmentIntervals() const;
std::vector<std::pair<double, double>> getSegmentIntervals() const;
bool hasCustomSegmentIntervals() const;
void setUnitSystemSpecificDefaults();
void updateFromTopLevelWell( const RimMswCompletionParameters* topLevelWellParameters );
@@ -125,4 +131,6 @@ private:
caf::PdmField<bool> m_enforceMaxSegmentLength;
caf::PdmField<double> m_maxSegmentLength;
caf::PdmChildField<RimCustomSegmentIntervalCollection*> m_customSegmentIntervals;
};