mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-26 07:00:17 -05:00
Implement timeline-based event system for managing temporal changes in well completions, perforations, valves, and production controls. Features: - Event timeline container with date-based querying - Six event types: PERF, VALVE, TUBING, WSTATE, WTYPE, WCONTROL - Schedule keyword generation (DATES, COMPDAT, WELSEGS, WCONPROD, WCONINJE) - Multi-well schedule generation support - Python GRPC API for event management - YAML configuration file support - Comprehensive test suite Implementation: - New directory: ApplicationLibCode/ProjectDataModel/WellEvents/ - Event classes inherit from RimWellEvent base class - RimWellEventTimeline integrated into RimWellPath - RicScheduleDataGenerator for multi-well schedule export - Python API: event_timeline(), add_perf_event(), add_valve_event(), etc. - Tests verify all event types and YAML config parsing #13425 Add date tracking to valves and diameter/roughness intervals Add creation date fields to RimWellPathValve and RimDiameterRoughnessInterval to support filtering objects by date during completion data export. - Add m_useCustomStartDate and m_startDate fields - Add enableCustomStartDate(), setCustomStartDate(), isActiveOnDate() methods - Set creation dates when applying valve and tubing events in RimWellEventTimeline #13425 Add date-based filtering for schedule data export Add optional export date parameter to MSW data collection functions to filter valves and perforations based on their creation dates during schedule export. - Add exportDate parameter to collectWsegvalvData, collectWsegAicdData, collectWsegSicdData, and collectCompsegData functions - Filter valves by checking isActiveOnDate() on the associated RimWellPathValve - Add RimPerforationInterval reference to RicMswPerforation for date filtering - Pass export date from RicScheduleDataGenerator through the extraction chain This ensures segment numbers reflect the full model while only including objects active on the export date in output keywords. Add test to prove compsegs changes over time. #13425 Fix mypy type checking errors in well_events.py - Add type annotations to all function parameters - Fix generic type hints (dict -> Dict[str, Any]) - Import Any type for proper type safety - Add type: ignore for method reassignment - All 31 tests pass #13425 Add schedule-level keyword events for non-well-specific keywords Add a new event type called "Keyword Event" for schedule-level Eclipse keywords (RPTRST, GRUPTREE, RPTSCHED, etc.) that are NOT tied to a specific well path. This complements the existing RimWellEventKeyword which operates at the well level. - Add RimKeywordEvent class inheriting from RimWellEvent with null wellPath - Add SCHEDULE_KEYWORD enum value to EventType - Add addKeywordEvent() method to RimWellEventTimeline - Add AddKeywordEvent Python API method to RimcWellEventTimeline - Update RicScheduleDataGenerator to output global keywords after well sections - Add Python wrapper add_keyword_event() with type inference - Add tests and examples for RPTRST, GRUPTREE keywords
1250 lines
46 KiB
C++
1250 lines
46 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2011- Statoil ASA
|
|
// Copyright (C) 2013- Ceetron Solutions AS
|
|
// Copyright (C) 2011-2012 Ceetron AS
|
|
//
|
|
// 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 "RimWellPathCollection.h"
|
|
|
|
#include "Cloud/RiaOsduConnector.h"
|
|
#include "RiaColorTables.h"
|
|
#include "RiaDateStringParser.h"
|
|
#include "RiaGuiApplication.h"
|
|
#include "RiaLogging.h"
|
|
#include "RiaPreferences.h"
|
|
#include "RiaTextStringTools.h"
|
|
#include "RiaWellNameComparer.h"
|
|
|
|
#include "RifOsduWellLogReader.h"
|
|
#include "RifOsduWellPathReader.h"
|
|
#include "RifWellPathFormationsImporter.h"
|
|
#include "RifWellPathImporter.h"
|
|
|
|
#include "RigEclipseCaseData.h"
|
|
#include "RigMainGrid.h"
|
|
#include "Well/RigOsduWellLogData.h"
|
|
#include "Well/RigWellPath.h"
|
|
|
|
#include "RimEclipseCase.h"
|
|
#include "RimEclipseCaseCollection.h"
|
|
#include "RimEclipseCaseTools.h"
|
|
#include "RimEclipseView.h"
|
|
#include "RimFileWellPath.h"
|
|
#include "RimModeledWellPath.h"
|
|
#include "RimMswSegmentCollection.h"
|
|
#include "RimOilField.h"
|
|
#include "RimOsduWellLog.h"
|
|
#include "RimOsduWellPath.h"
|
|
#include "RimPerforationCollection.h"
|
|
#include "RimProject.h"
|
|
#include "RimStimPlanModel.h"
|
|
#include "RimStimPlanModelCollection.h"
|
|
#include "RimWellLogLasFile.h"
|
|
#include "RimWellMeasurementCollection.h"
|
|
#include "RimWellPath.h"
|
|
#include "RimWellPathCompletionSettings.h"
|
|
#include "RimWellPathCompletions.h"
|
|
#include "RimWellPathTieIn.h"
|
|
|
|
#include "WellEvents/RimWellEventTimeline.h"
|
|
|
|
#include "RiuMainWindow.h"
|
|
|
|
#include "cafTreeNode.h" // TODO: Move to caf
|
|
|
|
#include "Riu3DMainWindowTools.h"
|
|
|
|
#include "cafDataLoadController.h"
|
|
#include "cafDataLoader.h"
|
|
#include "cafPdmFieldScriptingCapability.h"
|
|
#include "cafPdmObjectScriptingCapability.h"
|
|
#include "cafPdmUiEditorHandle.h"
|
|
#include "cafPdmUiTreeOrdering.h"
|
|
#include "cafProgressInfo.h"
|
|
|
|
#include <QCollator>
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
#include <QString>
|
|
|
|
#include <cmath>
|
|
#include <fstream>
|
|
#include <memory>
|
|
|
|
namespace caf
|
|
{
|
|
template <>
|
|
void RimWellPathCollection::WellVisibilityEnum::setUp()
|
|
{
|
|
addItem( RimWellPathCollection::FORCE_ALL_OFF, "FORCE_ALL_OFF", "Off" );
|
|
addItem( RimWellPathCollection::ALL_ON, "ALL_ON", "Individual" );
|
|
addItem( RimWellPathCollection::FORCE_ALL_ON, "FORCE_ALL_ON", "On" );
|
|
}
|
|
|
|
template <>
|
|
void RimWellPathCollection::MswGroupingEnum::setUp()
|
|
{
|
|
addItem( RimWellPathCollection::MswGroupingMode::USE_PREFERENCES, "USE_PREFERENCES", "Use Preferences" );
|
|
addItem( RimWellPathCollection::MswGroupingMode::CUSTOM, "CUSTOM", "Custom" );
|
|
addItem( RimWellPathCollection::MswGroupingMode::DISABLED, "DISABLED", "Disabled" );
|
|
setDefault( RimWellPathCollection::MswGroupingMode::USE_PREFERENCES );
|
|
}
|
|
|
|
} // namespace caf
|
|
|
|
CAF_PDM_SOURCE_INIT( RimWellPathCollection, "WellPaths" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimWellPathCollection::RimWellPathCollection()
|
|
{
|
|
CAF_PDM_InitScriptableObjectWithNameAndComment( "Wells", ":/WellCollection.png", "", "", "WellPathCollection", "Collection of Well Paths" );
|
|
|
|
CAF_PDM_InitField( &isActive, "Active", true, "Active" );
|
|
isActive.uiCapability()->setUiHidden( true );
|
|
|
|
CAF_PDM_InitField( &showWellPathLabel, "ShowWellPathLabel", true, "Show Well Path Labels" );
|
|
|
|
cvf::Color3f defWellLabelColor = RiaPreferences::current()->defaultWellLabelColor();
|
|
CAF_PDM_InitField( &wellPathLabelColor, "WellPathLabelColor", defWellLabelColor, "Well label color" );
|
|
|
|
CAF_PDM_InitField( &wellPathVisibility, "GlobalWellPathVisibility", WellVisibilityEnum( ALL_ON ), "Global Well Path Visibility" );
|
|
|
|
CAF_PDM_InitField( &wellPathRadiusScaleFactor, "WellPathRadiusScale", 0.1, "Well Path Radius Scale" );
|
|
CAF_PDM_InitField( &wellPathCrossSectionVertexCount, "WellPathVertexCount", 12, "Well Path Vertex Count" );
|
|
wellPathCrossSectionVertexCount.xmlCapability()->disableIO();
|
|
wellPathCrossSectionVertexCount.uiCapability()->setUiHidden( true );
|
|
|
|
CAF_PDM_InitField( &wellPathClip, "WellPathClip", true, "Clip Well Paths" );
|
|
CAF_PDM_InitField( &wellPathClipZDistance, "WellPathClipZDistance", 100, "Well Path Clipping Depth Distance" );
|
|
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_wellPaths, "WellPaths", "Well Paths" );
|
|
m_wellPaths.uiCapability()->setUiTreeChildrenHidden( true );
|
|
|
|
CAF_PDM_InitFieldNoDefault( &m_wellMeasurements, "WellMeasurements", "Measurements" );
|
|
m_wellMeasurements = new RimWellMeasurementCollection;
|
|
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_eventTimeline, "EventTimeline", "Event Timeline" );
|
|
m_eventTimeline = new RimWellEventTimeline;
|
|
|
|
CAF_PDM_InitFieldNoDefault( &m_wellPathNodes, "WellPathNodes", "Well Path Nodes" );
|
|
m_wellPathNodes.xmlCapability()->disableIO();
|
|
|
|
CAF_PDM_InitFieldNoDefault( &m_mswNameGrouping, "MswWellNameGrouping", "Grouping" );
|
|
CAF_PDM_InitFieldNoDefault( &m_mswNameGroupingPattern, "MswWellNameGroupingPattern", "Grouping Pattern" );
|
|
|
|
CAF_PDM_InitFieldNoDefault( &m_mswEclipseCase, "MswEclipseCase", "Eclipse Case" );
|
|
CAF_PDM_InitField( &m_mswShowBands, "MseShowBands", true, "Show Bands" );
|
|
|
|
m_wellPathImporter = std::make_unique<RifWellPathImporter>();
|
|
m_wellPathFormationsImporter = std::make_unique<RifWellPathFormationsImporter>();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimWellPathCollection::~RimWellPathCollection()
|
|
{
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
|
|
{
|
|
scheduleRedrawAffectedViews();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QList<caf::PdmOptionItemInfo> RimWellPathCollection::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
|
|
{
|
|
QList<caf::PdmOptionItemInfo> options;
|
|
|
|
if ( fieldNeedingOptions == &m_mswEclipseCase )
|
|
{
|
|
options.push_back( caf::PdmOptionItemInfo( "None", nullptr ) );
|
|
|
|
auto cases = RimEclipseCaseTools::eclipseCases();
|
|
for ( auto* c : cases )
|
|
{
|
|
options.push_back( caf::PdmOptionItemInfo( c->caseUserDescription(), c, false, c->uiIconProvider() ) );
|
|
}
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Read files containing well path data, or create geometry based on the targets
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimWellPathCollection::loadDataAndUpdate()
|
|
{
|
|
auto hasOsduData = []( const std::vector<RimWellPath*>& wellPaths ) -> bool
|
|
{
|
|
for ( RimWellPath* wellPath : wellPaths )
|
|
{
|
|
if ( dynamic_cast<RimOsduWellPath*>( wellPath ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
auto countWellLogs = []( const std::vector<RimWellPath*>& wellPaths ) -> bool
|
|
{
|
|
size_t count = 0;
|
|
for ( RimWellPath* wellPath : wellPaths )
|
|
{
|
|
count += wellPath->wellLogs().size();
|
|
}
|
|
return count;
|
|
};
|
|
|
|
caf::ProgressInfoEventProcessingBlocker blocker;
|
|
caf::ProgressInfo progress( allWellPaths().size() + countWellLogs( allWellPaths() ) + 2, "Reading well paths from file", false, true );
|
|
|
|
readWellPathFormationFiles();
|
|
|
|
progress.incrementProgress();
|
|
|
|
if ( hasOsduData( allWellPaths() ) )
|
|
{
|
|
if ( auto osduConnector = RiaApplication::instance()->makeOsduConnector() )
|
|
{
|
|
osduConnector->requestTokenWithCancelButton();
|
|
}
|
|
}
|
|
|
|
caf::DataLoadController* dataLoadController = caf::DataLoadController::instance();
|
|
|
|
const QString wellPathGeometryKeyword = "WELL_PATH_GEOMETRY";
|
|
const QString wellLogKeyword = "WELL_LOG";
|
|
|
|
progress.setProgressDescription( QString( "Reading well path geometry." ) );
|
|
for ( RimWellPath* wellPath : allWellPaths() )
|
|
{
|
|
dataLoadController->loadData( *wellPath, wellPathGeometryKeyword, progress );
|
|
}
|
|
dataLoadController->blockUntilDone( wellPathGeometryKeyword );
|
|
|
|
if ( progress.isCancelled() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
progress.setProgressDescription( QString( "Reading well logs." ) );
|
|
for ( RimWellPath* wellPath : allWellPaths() )
|
|
{
|
|
for ( RimWellLog* wellLog : wellPath->wellLogs() )
|
|
{
|
|
dataLoadController->loadData( *wellLog, wellLogKeyword, progress );
|
|
}
|
|
}
|
|
dataLoadController->blockUntilDone( wellLogKeyword );
|
|
|
|
progress.setProgressDescription( QString( "Reading additional data." ) );
|
|
for ( RimWellPath* wellPath : allWellPaths() )
|
|
{
|
|
RimStimPlanModelCollection* stimPlanModelCollection = wellPath->stimPlanModelCollection();
|
|
if ( stimPlanModelCollection )
|
|
{
|
|
for ( RimStimPlanModel* stimPlanModel : stimPlanModelCollection->allStimPlanModels() )
|
|
{
|
|
stimPlanModel->loadDataAndUpdate();
|
|
}
|
|
}
|
|
}
|
|
progress.incrementProgress();
|
|
|
|
rebuildWellPathNodes();
|
|
|
|
sortWellsByName();
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimWellPath*> RimWellPathCollection::addWellPaths( QStringList filePaths, QStringList* errorMessages )
|
|
{
|
|
CAF_ASSERT( errorMessages );
|
|
|
|
std::vector<RimFileWellPath*> wellPathArray;
|
|
|
|
for ( const QString& filePath : filePaths )
|
|
{
|
|
std::expected<std::vector<RimFileWellPath*>, QString> result = addWellPathsForFile( filePath );
|
|
if ( result )
|
|
{
|
|
wellPathArray.insert( wellPathArray.end(), result.value().begin(), result.value().end() );
|
|
}
|
|
else if ( errorMessages )
|
|
{
|
|
errorMessages->append( result.error() );
|
|
}
|
|
}
|
|
|
|
std::vector<RimWellPath*> addedWellPaths = readAndAddWellPaths( wellPathArray );
|
|
CAF_ASSERT( wellPathArray.empty() );
|
|
|
|
scheduleRedrawAffectedViews();
|
|
updateAllRequiredEditors();
|
|
|
|
return addedWellPaths;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::expected<std::vector<RimFileWellPath*>, QString> RimWellPathCollection::addWellPathsForFile( const QString& filePath )
|
|
{
|
|
auto isAlreadyLoaded = []( const QString& filePath, const std::vector<RimWellPath*>& wellPaths ) -> bool
|
|
{
|
|
for ( const auto& wellPath : wellPaths )
|
|
{
|
|
if ( auto* fWPath = dynamic_cast<RimFileWellPath*>( wellPath ) )
|
|
{
|
|
QFile f1;
|
|
f1.setFileName( filePath );
|
|
QFile f2;
|
|
f2.setFileName( fWPath->filePath() );
|
|
if ( f1.fileName() == f2.fileName() ) return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
// Check if this file is already open
|
|
bool isJsonFile = filePath.endsWith( "json", Qt::CaseInsensitive );
|
|
if ( isJsonFile && isAlreadyLoaded( filePath, m_wellPaths.childrenByType() ) )
|
|
{
|
|
return std::unexpected( QString( "%1 is already loaded" ).arg( filePath ) );
|
|
}
|
|
|
|
std::vector<RimFileWellPath*> wellPaths;
|
|
if ( isJsonFile )
|
|
{
|
|
auto* wellPath = new RimFileWellPath();
|
|
wellPath->setFilepath( filePath );
|
|
wellPaths.push_back( wellPath );
|
|
}
|
|
else
|
|
{
|
|
// Create Well path objects for all the paths in the assumed ascii file
|
|
size_t wellPathCount = m_wellPathImporter->wellDataCount( filePath );
|
|
for ( size_t i = 0; i < wellPathCount; ++i )
|
|
{
|
|
auto* wellPath = new RimFileWellPath();
|
|
wellPath->setFilepath( filePath );
|
|
wellPath->setWellPathIndexInFile( static_cast<int>( i ) );
|
|
wellPaths.push_back( wellPath );
|
|
}
|
|
}
|
|
|
|
return wellPaths;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::addWellPath( gsl::not_null<RimWellPath*> wellPath )
|
|
{
|
|
m_wellPaths.push_back( wellPath );
|
|
|
|
rebuildWellPathNodes();
|
|
|
|
m_mostRecentlyUpdatedWellPath = wellPath;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimWellPath*> RimWellPathCollection::allWellPaths() const
|
|
{
|
|
return m_wellPaths.childrenByType();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimWellPath*> RimWellPathCollection::readAndAddWellPaths( std::vector<RimFileWellPath*>& wellPathArray )
|
|
{
|
|
caf::ProgressInfo progress( wellPathArray.size(), "Reading well paths from file" );
|
|
|
|
// Keep track of the well paths that were actually added.
|
|
std::vector<RimWellPath*> addedWellPaths;
|
|
for ( RimFileWellPath* wellPath : wellPathArray )
|
|
{
|
|
wellPath->readWellPathFile( nullptr, m_wellPathImporter.get(), true );
|
|
|
|
progress.setProgressDescription( QString( "Reading file %1" ).arg( wellPath->name() ) );
|
|
|
|
wellPath->setWellPathColor( RiaColorTables::wellPathsPaletteColors().cycledColor3f( m_wellPaths.size() ) );
|
|
wellPath->setUnitSystem( findUnitSystemForWellPath( wellPath ) );
|
|
addWellPath( wellPath );
|
|
addedWellPaths.push_back( wellPath );
|
|
|
|
progress.incrementProgress();
|
|
}
|
|
wellPathArray.clear(); // This should not be used again. We may have deleted items
|
|
|
|
groupWellPaths( allWellPaths() );
|
|
sortWellsByName();
|
|
rebuildWellPathNodes();
|
|
|
|
return addedWellPaths;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::addWellPaths( const std::vector<RimWellPath*> incomingWellPaths )
|
|
{
|
|
for ( const auto& wellPath : incomingWellPaths )
|
|
{
|
|
addWellPath( wellPath );
|
|
}
|
|
|
|
groupWellPaths( allWellPaths() );
|
|
sortWellsByName();
|
|
rebuildWellPathNodes();
|
|
|
|
updateAllRequiredEditors();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimWellLogLasFile*> RimWellPathCollection::addWellLogs( const QStringList& filePaths, QStringList* errorMessages )
|
|
{
|
|
CAF_ASSERT( errorMessages );
|
|
|
|
std::vector<RimWellLogLasFile*> logFileInfos;
|
|
|
|
for ( const QString& filePath : filePaths )
|
|
{
|
|
QString errorMessage;
|
|
RimWellLogLasFile* logFileInfo = RimWellLogLasFile::readWellLogFile( filePath, &errorMessage );
|
|
if ( !errorMessage.isEmpty() )
|
|
{
|
|
errorMessages->push_back( errorMessage );
|
|
}
|
|
if ( logFileInfo )
|
|
{
|
|
RimWellPath* wellPath = tryFindMatchingWellPath( logFileInfo->wellName() );
|
|
if ( !wellPath )
|
|
{
|
|
wellPath = new RimWellPath();
|
|
addWellPath( wellPath );
|
|
}
|
|
|
|
wellPath->addWellLog( logFileInfo );
|
|
logFileInfos.push_back( logFileInfo );
|
|
}
|
|
}
|
|
|
|
sortWellsByName();
|
|
updateAllRequiredEditors();
|
|
|
|
return logFileInfos;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::addWellLog( RimWellLog* wellLog, RimWellPath* wellPath )
|
|
{
|
|
wellPath->addWellLog( wellLog );
|
|
sortWellsByName();
|
|
updateAllRequiredEditors();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::addWellPathFormations( const QStringList& filePaths )
|
|
{
|
|
QString outputMessage = "Well Picks Import\n";
|
|
outputMessage += "-----------------------------------------------\n";
|
|
outputMessage += "Well Name \tDetected Well Path \tCount\n";
|
|
|
|
bool fileReadSuccess = false;
|
|
|
|
for ( const QString& filePath : filePaths )
|
|
{
|
|
std::map<QString, cvf::ref<RigWellPathFormations>> newFormations =
|
|
m_wellPathFormationsImporter->readWellPathFormationsFromPath( filePath );
|
|
|
|
for ( const auto& newFormation : newFormations )
|
|
{
|
|
fileReadSuccess = true;
|
|
|
|
RimWellPath* wellPath = tryFindMatchingWellPath( newFormation.first );
|
|
if ( !wellPath )
|
|
{
|
|
wellPath = new RimWellPath();
|
|
wellPath->setName( newFormation.first );
|
|
addWellPath( wellPath );
|
|
RiaLogging::info( QString( "Created new well: %1" ).arg( wellPath->name() ) );
|
|
}
|
|
wellPath->setFormationsGeometry( newFormation.second );
|
|
|
|
QString wellFormationsCount = QString( "%1" ).arg( newFormation.second->formationNamesCount() );
|
|
|
|
m_mostRecentlyUpdatedWellPath = wellPath;
|
|
|
|
outputMessage += newFormation.first + "\t\t";
|
|
outputMessage += wellPath->name() + " \t\t\t";
|
|
outputMessage += wellFormationsCount + "\n";
|
|
}
|
|
}
|
|
outputMessage += "-----------------------------------------------";
|
|
|
|
if ( fileReadSuccess )
|
|
{
|
|
RiaLogging::errorInMessageBox( Riu3DMainWindowTools::mainWindowWidget(), "Well Picks Import", outputMessage );
|
|
}
|
|
|
|
sortWellsByName();
|
|
updateAllRequiredEditors();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
|
|
{
|
|
caf::PdmUiGroup* wellHeadGroup = uiOrdering.addNewGroup( "Well labels" );
|
|
wellHeadGroup->add( &showWellPathLabel );
|
|
wellHeadGroup->add( &wellPathLabelColor );
|
|
|
|
caf::PdmUiGroup* wellPipe = uiOrdering.addNewGroup( "Well pipe" );
|
|
wellPipe->add( &wellPathVisibility );
|
|
wellPipe->add( &wellPathRadiusScaleFactor );
|
|
|
|
caf::PdmUiGroup* advancedGroup = uiOrdering.addNewGroup( "Clipping" );
|
|
advancedGroup->add( &wellPathClip );
|
|
advancedGroup->add( &wellPathClipZDistance );
|
|
|
|
caf::PdmUiGroup* wellGrouping = uiOrdering.addNewGroup( "MSW Grouping" );
|
|
wellGrouping->add( &m_mswNameGrouping );
|
|
if ( m_mswNameGrouping() != MswGroupingMode::DISABLED )
|
|
{
|
|
if ( m_mswNameGrouping() == MswGroupingMode::USE_PREFERENCES )
|
|
{
|
|
m_mswNameGroupingPattern = RiaPreferences::current()->multiLateralWellNamePattern();
|
|
}
|
|
wellGrouping->add( &m_mswNameGroupingPattern );
|
|
m_mswNameGroupingPattern.uiCapability()->setUiReadOnly( m_mswNameGrouping() == MswGroupingMode::USE_PREFERENCES );
|
|
}
|
|
|
|
caf::PdmUiGroup* mswSegmentGroup = uiOrdering.addNewGroup( "MSW Segment Visualization" );
|
|
mswSegmentGroup->add( &m_mswShowBands );
|
|
mswSegmentGroup->add( &m_mswEclipseCase );
|
|
mswSegmentGroup->addNewButton( "Update Segments", [this]() { updateMswSegments(); } );
|
|
uiOrdering.skipRemainingFields();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName /*= ""*/ )
|
|
{
|
|
if ( !m_wellMeasurements->isEmpty() )
|
|
{
|
|
uiTreeOrdering.add( &m_wellMeasurements );
|
|
}
|
|
|
|
if ( m_eventTimeline() && m_eventTimeline()->eventCount() > 0 )
|
|
{
|
|
uiTreeOrdering.add( &m_eventTimeline );
|
|
}
|
|
|
|
for ( const auto& wellPathNode : m_wellPathNodes() )
|
|
{
|
|
RimWellPathCollection::buildUiTreeOrdering( wellPathNode, &uiTreeOrdering, uiConfigName );
|
|
}
|
|
|
|
uiTreeOrdering.skipRemainingChildren( true );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
caf::PdmFieldHandle* RimWellPathCollection::objectToggleField()
|
|
{
|
|
return &isActive;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::updateMswSegments()
|
|
{
|
|
for ( const auto& wellPath : m_wellPaths )
|
|
{
|
|
if ( !wellPath ) continue;
|
|
|
|
auto* completions = wellPath->completions();
|
|
if ( !completions ) continue;
|
|
|
|
auto* segmentCollection = completions->mswSegmentCollection();
|
|
if ( !segmentCollection ) continue;
|
|
|
|
segmentCollection->updateSegments( m_mswEclipseCase() );
|
|
}
|
|
|
|
if ( RimProject* project = RimProject::current() )
|
|
{
|
|
project->scheduleCreateDisplayModelAndRedrawAllViews();
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimWellPathCollection::showMswSegmentBands() const
|
|
{
|
|
return m_mswShowBands();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::scheduleRedrawAffectedViews()
|
|
{
|
|
RimProject* proj = RimProject::current();
|
|
if ( proj ) proj->reloadCompletionTypeResultsInAllViews();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimWellPathCollection::anyWellsContainingPerforationIntervals() const
|
|
{
|
|
for ( const auto& wellPath : m_wellPaths )
|
|
{
|
|
if ( !wellPath->perforationIntervalCollection()->perforations().empty() ) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
size_t RimWellPathCollection::modelledWellPathCount() const
|
|
{
|
|
size_t count = 0;
|
|
for ( const auto& wellPath : m_wellPaths )
|
|
{
|
|
if ( dynamic_cast<const RimModeledWellPath*>( wellPath.p() ) )
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimWellPath* RimWellPathCollection::wellPathByName( const QString& wellPathName ) const
|
|
{
|
|
for ( const auto& wellPath : m_wellPaths )
|
|
{
|
|
if ( wellPath->name() == wellPathName )
|
|
{
|
|
return wellPath;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimWellPath* RimWellPathCollection::tryFindMatchingWellPath( const QString& wellName ) const
|
|
{
|
|
QString matchedWellPath = RiaWellNameComparer::tryFindMatchingWellPath( wellName );
|
|
|
|
return !matchedWellPath.isEmpty() ? wellPathByName( matchedWellPath ) : nullptr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::deleteAllWellPaths()
|
|
{
|
|
m_wellPaths.deleteChildren();
|
|
m_wellPathNodes.deleteChildren();
|
|
|
|
m_wellPathImporter->clear();
|
|
updateAllRequiredEditors();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::deleteWell( RimWellPath* wellPath )
|
|
{
|
|
removeWellPath( wellPath );
|
|
|
|
m_wellPaths.removeChild( wellPath );
|
|
delete wellPath;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::setMswWellPattern( const QString& pattern )
|
|
{
|
|
if ( pattern.isEmpty() )
|
|
{
|
|
m_mswNameGrouping = MswGroupingMode::DISABLED;
|
|
m_mswNameGroupingPattern = "";
|
|
}
|
|
else
|
|
{
|
|
m_mswNameGrouping = MswGroupingMode::CUSTOM;
|
|
m_mswNameGroupingPattern = pattern;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::groupWellPaths( const std::vector<RimWellPath*>& wellPaths )
|
|
{
|
|
const auto& rootWells = wellPathsForWellNameStem( wellPaths );
|
|
|
|
for ( const auto& [groupName, wellPathsInGroup] : rootWells )
|
|
{
|
|
if ( groupName == unGroupedText() ) continue;
|
|
|
|
for ( const auto& wellPathToConnect : wellPathsInGroup )
|
|
{
|
|
// Assign the group names as well name for export
|
|
wellPathToConnect->completionSettings()->setWellNameForExport( groupName );
|
|
|
|
const auto wellPathGeometry = wellPathToConnect->wellPathGeometry();
|
|
if ( wellPathGeometry )
|
|
{
|
|
std::map<RimWellPath*, double> sharedWellPathLengths;
|
|
|
|
for ( const auto& otherWellPath : wellPathsInGroup )
|
|
{
|
|
if ( otherWellPath == wellPathToConnect ) continue;
|
|
|
|
if ( otherWellPath && otherWellPath->wellPathGeometry() )
|
|
{
|
|
const double sharedWellPathLength = otherWellPath->wellPathGeometry()->identicalTubeLength( *wellPathGeometry );
|
|
const double eps = 1.0e-2;
|
|
if ( sharedWellPathLength > eps )
|
|
{
|
|
sharedWellPathLengths[otherWellPath] = sharedWellPathLength;
|
|
}
|
|
}
|
|
}
|
|
|
|
RimWellPath* longestSharedWellPath = nullptr;
|
|
double longestSharedWellPathLength = 0.0;
|
|
for ( const auto& [wellPathCandidate, sharedWellPathLength] : sharedWellPathLengths )
|
|
{
|
|
if ( wellPathCandidate )
|
|
{
|
|
const double distanceDifference = fabs( sharedWellPathLength - longestSharedWellPathLength );
|
|
|
|
const double differenceThreshold = 1.0;
|
|
if ( longestSharedWellPath && ( distanceDifference < differenceThreshold ) )
|
|
{
|
|
// If the main well is named WELL_A, each side steps of a MSW can be given the following names
|
|
// WELL_A_Y1
|
|
// WELL_A_Y2
|
|
// WELL_A_Y3
|
|
//
|
|
// If Y3 has equal shared geometry with both Y2 and Y1, make sure that Y3 is connected to Y1.
|
|
if ( wellPathCandidate->name() < longestSharedWellPath->name() )
|
|
{
|
|
longestSharedWellPath = wellPathCandidate;
|
|
longestSharedWellPathLength = sharedWellPathLength;
|
|
}
|
|
}
|
|
else if ( sharedWellPathLength > longestSharedWellPathLength )
|
|
{
|
|
longestSharedWellPath = wellPathCandidate;
|
|
longestSharedWellPathLength = sharedWellPathLength;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( longestSharedWellPath )
|
|
{
|
|
if ( wellPathToConnect->name() > longestSharedWellPath->name() )
|
|
{
|
|
wellPathToConnect->connectWellPaths( longestSharedWellPath, longestSharedWellPathLength );
|
|
}
|
|
else
|
|
{
|
|
longestSharedWellPath->connectWellPaths( wellPathToConnect, longestSharedWellPathLength );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimWellPath* RimWellPathCollection::mostRecentlyUpdatedWellPath()
|
|
{
|
|
return m_mostRecentlyUpdatedWellPath;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::readWellPathFormationFiles()
|
|
{
|
|
for ( const auto& wellPath : m_wellPaths )
|
|
{
|
|
QString errorMessage;
|
|
if ( !wellPath->readWellPathFormationsFile( &errorMessage, m_wellPathFormationsImporter.get() ) )
|
|
{
|
|
RiaLogging::errorInMessageBox( Riu3DMainWindowTools::mainWindowWidget(), "File open error", errorMessage );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::reloadAllWellPathFormations()
|
|
{
|
|
caf::ProgressInfo progress( m_wellPaths.size(), "Reloading well picks from file" );
|
|
|
|
for ( const auto& wellPath : m_wellPaths )
|
|
{
|
|
QString errorMessage;
|
|
if ( !wellPath->reloadWellPathFormationsFile( &errorMessage, m_wellPathFormationsImporter.get() ) )
|
|
{
|
|
RiaLogging::errorInMessageBox( Riu3DMainWindowTools::mainWindowWidget(), "File open error", errorMessage );
|
|
}
|
|
|
|
progress.setProgressDescription( QString( "Reloading formation file for %1" ).arg( wellPath->name() ) );
|
|
progress.incrementProgress();
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::removeWellPath( gsl::not_null<RimWellPath*> wellToRemove )
|
|
{
|
|
auto* fileWellToRemove = dynamic_cast<RimFileWellPath*>( wellToRemove.get() );
|
|
if ( fileWellToRemove )
|
|
{
|
|
bool isFilePathUsed = false;
|
|
for ( const auto& well : m_wellPaths )
|
|
{
|
|
auto fileWell = dynamic_cast<RimFileWellPath*>( well.p() );
|
|
if ( fileWell == fileWellToRemove ) continue;
|
|
|
|
if ( fileWell && fileWell->filePath() == fileWellToRemove->filePath() )
|
|
{
|
|
isFilePathUsed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !isFilePathUsed )
|
|
{
|
|
// One file can have multiple well paths
|
|
// If no other well paths are referencing the filepath, remove cached data from the file reader
|
|
m_wellPathImporter->removeFilePath( fileWellToRemove->filePath() );
|
|
}
|
|
}
|
|
updateAllRequiredEditors();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool lessWellPath( const caf::PdmPointer<RimWellPath>& w1, const caf::PdmPointer<RimWellPath>& w2 )
|
|
{
|
|
if ( w1.notNull() && w2.notNull() )
|
|
{
|
|
// Use QCollator to sort integer values in addition to text
|
|
|
|
QCollator collator;
|
|
collator.setNumericMode( true );
|
|
return collator.compare( w1->name(), w2->name() ) < 0;
|
|
}
|
|
|
|
if ( w1.notNull() ) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::sortWellsByName()
|
|
{
|
|
std::sort( m_wellPaths.begin(), m_wellPaths.end(), lessWellPath );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
caf::AppEnum<RiaDefines::EclipseUnitSystem> RimWellPathCollection::findUnitSystemForWellPath( const RimWellPath* wellPath )
|
|
{
|
|
if ( !wellPath || !wellPath->wellPathGeometry() ) return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
|
|
|
|
RimProject* project = RimProject::current();
|
|
if ( project->activeOilField()->analysisModels->cases.empty() )
|
|
{
|
|
return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
|
|
}
|
|
|
|
const RigEclipseCaseData* eclipseCaseData = project->activeOilField()->analysisModels->cases()[0]->eclipseCaseData();
|
|
if ( !eclipseCaseData || !eclipseCaseData->mainGrid() )
|
|
{
|
|
return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
|
|
}
|
|
|
|
cvf::BoundingBox caseBoundingBox = eclipseCaseData->mainGrid()->boundingBox();
|
|
cvf::BoundingBox wellPathBoundingBox;
|
|
for ( const auto& wellPathPoint : wellPath->wellPathGeometry()->wellPathPoints() )
|
|
{
|
|
wellPathBoundingBox.add( wellPathPoint );
|
|
}
|
|
|
|
if ( wellPathBoundingBox.isValid() && caseBoundingBox.isValid() && caseBoundingBox.intersects( wellPathBoundingBox ) )
|
|
{
|
|
return eclipseCaseData->unitsType();
|
|
}
|
|
|
|
return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::rebuildWellPathNodes()
|
|
{
|
|
m_wellPathNodes.deleteChildren();
|
|
|
|
std::map<QString, std::vector<RimWellPath*>> rootWells = wellPathsForWellNameStem( m_wellPaths.childrenByType() );
|
|
for ( auto [groupName, wellPathGroup] : rootWells )
|
|
{
|
|
if ( groupName == unGroupedText() )
|
|
{
|
|
// For single wells, create well paths directly in the well collection folder
|
|
|
|
for ( auto wellPath : wellPathGroup )
|
|
{
|
|
if ( !wellPath ) continue;
|
|
if ( !wellPath->isTopLevelWellPath() ) continue;
|
|
|
|
auto node = addWellToWellNode( nullptr, wellPath );
|
|
m_wellPathNodes.push_back( node );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Create a group node with group name and put related wells into this group
|
|
|
|
auto rootNode = new cafNamedTreeNode;
|
|
rootNode->setName( groupName );
|
|
rootNode->setIcon( ":/WellPathGroup.svg" );
|
|
m_wellPathNodes.push_back( rootNode );
|
|
|
|
for ( auto wellPath : wellPathsWithNoParent( wellPathGroup ) )
|
|
{
|
|
if ( !wellPath ) continue;
|
|
|
|
addWellToWellNode( rootNode, wellPath );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
cafTreeNode* RimWellPathCollection::addWellToWellNode( cafTreeNode* parent, RimWellPath* wellPath )
|
|
{
|
|
if ( wellPath == nullptr ) return nullptr;
|
|
|
|
std::vector<RimWellPath*> wellPaths = connectedWellPathLaterals( wellPath );
|
|
|
|
auto node = new cafObjectReferenceTreeNode;
|
|
node->setReferencedObject( wellPath );
|
|
if ( parent ) parent->addChild( node );
|
|
|
|
for ( auto w : wellPaths )
|
|
{
|
|
addWellToWellNode( node, w );
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimWellPath*> RimWellPathCollection::wellPathsWithNoParent( const std::vector<RimWellPath*>& sourceWellPaths ) const
|
|
{
|
|
std::vector<RimWellPath*> wellPaths;
|
|
|
|
for ( const auto& w : sourceWellPaths )
|
|
{
|
|
if ( !w ) continue;
|
|
|
|
if ( w->isTopLevelWellPath() )
|
|
{
|
|
wellPaths.push_back( w );
|
|
}
|
|
}
|
|
|
|
return wellPaths;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimWellPath*> RimWellPathCollection::connectedWellPathLaterals( const RimWellPath* parentWellPath ) const
|
|
{
|
|
if ( !parentWellPath ) return {};
|
|
|
|
std::vector<RimWellPath*> wellPaths;
|
|
|
|
for ( const auto& w : m_wellPaths )
|
|
{
|
|
if ( !w ) continue;
|
|
|
|
if ( w->wellPathTieIn() && ( w->wellPathTieIn()->parentWell() == parentWellPath ) )
|
|
{
|
|
wellPaths.push_back( w );
|
|
}
|
|
}
|
|
|
|
return wellPaths;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::map<QString, std::vector<RimWellPath*>>
|
|
RimWellPathCollection::wellPathsForWellNameStem( const std::vector<RimWellPath*>& sourceWellPaths ) const
|
|
{
|
|
auto getMultiLateralWellPathPattern = [this]() -> QString
|
|
{
|
|
if ( m_mswNameGrouping() == MswGroupingMode::DISABLED )
|
|
{
|
|
return "";
|
|
}
|
|
else if ( m_mswNameGrouping() == MswGroupingMode::USE_PREFERENCES )
|
|
{
|
|
return RiaPreferences::current()->multiLateralWellNamePattern();
|
|
}
|
|
else
|
|
{
|
|
return m_mswNameGroupingPattern();
|
|
}
|
|
};
|
|
|
|
QString multiLateralWellPathPattern = getMultiLateralWellPathPattern();
|
|
|
|
QString regexPattern = QRegularExpression::wildcardToRegularExpression( multiLateralWellPathPattern );
|
|
QRegularExpression re( regexPattern, QRegularExpression::CaseInsensitiveOption );
|
|
std::map<QString, std::vector<RimWellPath*>> rootWells;
|
|
|
|
for ( auto wellPath : sourceWellPaths )
|
|
{
|
|
QString name = wellPath->name();
|
|
if ( re.match( name ).hasMatch() )
|
|
{
|
|
int indexOfLateralStart = name.indexOf( 'Y' );
|
|
if ( indexOfLateralStart > 0 )
|
|
{
|
|
QString rootWellName = wellPath->name().left( indexOfLateralStart );
|
|
|
|
rootWells[rootWellName].push_back( wellPath );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
rootWells[RimWellPathCollection::unGroupedText()].push_back( wellPath );
|
|
}
|
|
|
|
for ( auto [groupName, wellPaths] : rootWells )
|
|
{
|
|
std::sort( wellPaths.begin(), wellPaths.end(), lessWellPath );
|
|
}
|
|
|
|
return rootWells;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::buildUiTreeOrdering( cafTreeNode* treeNode, caf::PdmUiTreeOrdering* parentUiTreeNode, const QString& uiConfigName )
|
|
{
|
|
CAF_ASSERT( treeNode );
|
|
CAF_ASSERT( parentUiTreeNode );
|
|
|
|
if ( auto obj = treeNode->referencedObject() )
|
|
{
|
|
// Create the standard uiTreeOrdering with no relationship to parent using the helper function in
|
|
// PdmUiObjectHandle. This will ensure that any child objects will be created correctly including any
|
|
// recursive children. If a PdmUiTreeOrdering is created directly based on the PdmObject, andy child objects
|
|
// must be added manually.
|
|
|
|
auto uiTreeNode = obj->uiCapability()->uiTreeOrdering( uiConfigName );
|
|
parentUiTreeNode->appendChild( uiTreeNode );
|
|
|
|
// Build additional child nodes recursively
|
|
for ( auto childNode : treeNode->childNodes() )
|
|
{
|
|
buildUiTreeOrdering( childNode, uiTreeNode, uiConfigName );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If no referenced object is attached, fallback to the node. Not sure if this is the best solution
|
|
auto uiTreeNode = parentUiTreeNode->add( treeNode );
|
|
|
|
// Build additional child nodes recursively
|
|
for ( auto childNode : treeNode->childNodes() )
|
|
{
|
|
buildUiTreeOrdering( childNode, uiTreeNode, uiConfigName );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimWellPathCollection::unGroupedText()
|
|
{
|
|
return "UnGrouped";
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimWellMeasurementCollection* RimWellPathCollection::measurementCollection()
|
|
{
|
|
return m_wellMeasurements;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const RimWellMeasurementCollection* RimWellPathCollection::measurementCollection() const
|
|
{
|
|
return m_wellMeasurements;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimWellEventTimeline* RimWellPathCollection::eventTimeline()
|
|
{
|
|
return m_eventTimeline;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const RimWellEventTimeline* RimWellPathCollection::eventTimeline() const
|
|
{
|
|
return m_eventTimeline;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::onChildDeleted( caf::PdmChildArrayFieldHandle* childArray, std::vector<caf::PdmObjectHandle*>& referringObjects )
|
|
{
|
|
rebuildWellPathNodes();
|
|
|
|
scheduleRedrawAffectedViews();
|
|
uiCapability()->updateConnectedEditors();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::onChildAdded( caf::PdmFieldHandle* containerForNewObject )
|
|
{
|
|
rebuildWellPathNodes();
|
|
|
|
scheduleRedrawAffectedViews();
|
|
uiCapability()->updateConnectedEditors();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::pair<cvf::ref<RigWellPath>, QString> RimWellPathCollection::loadWellPathGeometryFromOsdu( RiaOsduConnector* osduConnector,
|
|
const QString& wellboreTrajectoryId,
|
|
double datumElevation )
|
|
{
|
|
auto [fileContents, errorMessage] = osduConnector->requestWellboreTrajectoryParquetDataByIdBlocking( wellboreTrajectoryId );
|
|
if ( !errorMessage.isEmpty() )
|
|
{
|
|
return { nullptr, errorMessage };
|
|
}
|
|
|
|
return RifOsduWellPathReader::readWellPathData( fileContents, datumElevation );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathCollection::initAfterRead()
|
|
{
|
|
if ( m_mswNameGrouping() == MswGroupingMode::USE_PREFERENCES )
|
|
{
|
|
m_mswNameGroupingPattern = RiaPreferences::current()->multiLateralWellNamePattern();
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::pair<cvf::ref<RigOsduWellLogData>, QString> RimWellPathCollection::loadWellLogFromOsdu( RiaOsduConnector* osduConnector,
|
|
const QString& wellLogId )
|
|
{
|
|
auto [fileContents, errorMessage] = osduConnector->requestWellLogParquetDataByIdBlocking( wellLogId );
|
|
if ( !errorMessage.isEmpty() )
|
|
{
|
|
return { nullptr, errorMessage };
|
|
}
|
|
|
|
return RifOsduWellLogReader::readWellLogData( fileContents );
|
|
}
|