Improve how wells are connected during import

When importing new wells, consider all wells when connecting multi segment wells
Make sure that import of individual well paths will give the same ordering as importing all wells in a single operation.
This commit is contained in:
Magne Sjaastad 2023-03-07 12:51:14 +01:00 committed by GitHub
parent fc7bde8d35
commit 51331facac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 98 additions and 88 deletions

View File

@ -913,8 +913,7 @@ bool RiaApplication::openOdbCaseFromFile( const QString& fileName, bool applyTim
//--------------------------------------------------------------------------------------------------
/// Add a list of well path file paths (JSON files) to the well path collection
//--------------------------------------------------------------------------------------------------
std::vector<RimWellPath*>
RiaApplication::addWellPathsToModel( QList<QString> wellPathFilePaths, bool importGrouped, gsl::not_null<QStringList*> errorMessages )
std::vector<RimWellPath*> RiaApplication::addWellPathsToModel( QList<QString> wellPathFilePaths, gsl::not_null<QStringList*> errorMessages )
{
if ( m_project == nullptr || m_project->oilFields.size() < 1 ) return {};
@ -932,7 +931,7 @@ std::vector<RimWellPath*>
std::vector<RimWellPath*> wellPaths;
if ( oilField->wellPathCollection )
{
wellPaths = oilField->wellPathCollection->addWellPaths( wellPathFilePaths, importGrouped, errorMessages );
wellPaths = oilField->wellPathCollection->addWellPaths( wellPathFilePaths, errorMessages );
}
oilField->wellPathCollection->updateConnectedEditors();

View File

@ -147,8 +147,7 @@ public:
bool openOdbCaseFromFile( const QString& fileName, bool applyTimeStepFilter = false );
std::vector<RimWellPath*>
addWellPathsToModel( QList<QString> wellPathFilePaths, bool importGrouped, gsl::not_null<QStringList*> errorMessages );
std::vector<RimWellPath*> addWellPathsToModel( QList<QString> wellPathFilePaths, gsl::not_null<QStringList*> errorMessages );
void addWellPathFormationsToModel( QList<QString> wellPathFilePaths );
std::vector<RimWellLogFile*> addWellLogsToModel( const QList<QString>& wellLogFilePaths, gsl::not_null<QStringList*> errorMessages );

View File

@ -81,7 +81,7 @@ RimStimPlanModel* RicNewStimPlanModelFeature::addStimPlanModel( RimWellPath*
stimPlanModel->setThicknessDirectionWellPath( thicknessDirectionWellPath );
std::vector<RimWellPath*> wellPaths = { thicknessDirectionWellPath };
wellPathCollection->addWellPaths( wellPaths, false );
wellPathCollection->addWellPaths( wellPaths );
if ( project )
{

View File

@ -127,9 +127,8 @@ void RicCreateEnsembleWellLogFeature::executeCommand( const RicCreateEnsembleWel
// Load well path from file
QStringList wellPathFilePaths;
wellPathFilePaths << ui.wellPathFilePath();
bool importGrouped = false;
QStringList errorMessages;
std::vector<RimWellPath*> wellPaths = RicImportWellPaths::importWellPaths( wellPathFilePaths, importGrouped, &errorMessages );
std::vector<RimWellPath*> wellPaths = RicImportWellPaths::importWellPaths( wellPathFilePaths, &errorMessages );
if ( wellPaths.empty() ) return;
wellPath = wellPaths[0];

View File

@ -118,7 +118,7 @@ void RicWellPathsImportSsihubFeature::onActionTriggered( bool isChecked )
if ( wellPaths.size() > 0 )
{
QStringList errorMessages;
app->addWellPathsToModel( wellPaths, false, &errorMessages );
app->addWellPathsToModel( wellPaths, &errorMessages );
app->project()->scheduleCreateDisplayModelAndRedrawAllViews();
}

View File

@ -163,7 +163,7 @@ void RicCreateMultipleWellPathLaterals::slotAppendFractures()
newModeledWellPath->wellPathTieIn()->setTieInMeasuredDepth( measuredDepth );
wellPathCollection->addWellPath( newModeledWellPath, false );
wellPathCollection->addWellPath( newModeledWellPath );
newModeledWellPath->resolveReferencesRecursively();
newModeledWellPath->updateReferencePoint();

View File

@ -66,7 +66,6 @@ RicImportWellPaths::RicImportWellPaths()
{
CAF_PDM_InitScriptableFieldNoDefault( &m_wellPathFolder, "wellPathFolder", "" );
CAF_PDM_InitScriptableFieldNoDefault( &m_wellPathFiles, "wellPathFiles", "" );
CAF_PDM_InitScriptableField( &m_importGrouped, "importGrouped", false, "" );
}
//--------------------------------------------------------------------------------------------------
@ -124,7 +123,7 @@ caf::PdmScriptResponse RicImportWellPaths::execute()
caf::PdmScriptResponse response;
if ( !wellPathFiles.empty() )
{
std::vector<RimWellPath*> importedWellPaths = importWellPaths( wellPathFiles, m_importGrouped(), &warningMessages );
std::vector<RimWellPath*> importedWellPaths = importWellPaths( wellPathFiles, &warningMessages );
if ( !importedWellPaths.empty() )
{
RicImportWellPathsResult* wellPathsResult = new RicImportWellPathsResult;
@ -157,15 +156,14 @@ caf::PdmScriptResponse RicImportWellPaths::execute()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimWellPath*>
RicImportWellPaths::importWellPaths( const QStringList& wellPathFilePaths, bool importGrouped, QStringList* errorMessages )
std::vector<RimWellPath*> RicImportWellPaths::importWellPaths( const QStringList& wellPathFilePaths, QStringList* errorMessages )
{
RiaApplication* app = RiaApplication::instance();
// Remember the path to next time
app->setLastUsedDialogDirectory( "WELLPATH_DIR", QFileInfo( wellPathFilePaths.last() ).absolutePath() );
std::vector<RimWellPath*> wellPaths = app->addWellPathsToModel( wellPathFilePaths, importGrouped, errorMessages );
std::vector<RimWellPath*> wellPaths = app->addWellPathsToModel( wellPathFilePaths, errorMessages );
RimProject* project = app->project();

View File

@ -42,7 +42,7 @@ public:
RicImportWellPaths();
caf::PdmScriptResponse execute() override;
static std::vector<RimWellPath*> importWellPaths( const QStringList& wellPathFilePaths, bool importGrouped, QStringList* errorMessages );
static std::vector<RimWellPath*> importWellPaths( const QStringList& wellPathFilePaths, QStringList* errorMessages );
protected:
static QStringList wellPathNameFilters();
@ -54,5 +54,4 @@ protected:
protected:
caf::PdmField<QString> m_wellPathFolder;
caf::PdmField<std::vector<QString>> m_wellPathFiles;
caf::PdmField<bool> m_importGrouped;
};

View File

@ -80,7 +80,7 @@ void RicNewEditableWellPathFeature::onActionTriggered( bool isChecked )
newWellPaths.back()->setName( "Well-" + QString::number( modelledWellpathCount + 1 ) );
newModeledWellPath->setWellPathColor( RiaColorTables::editableWellPathsPaletteColors().cycledColor3f( modelledWellpathCount ) );
wellPathCollection->addWellPaths( newWellPaths, false );
wellPathCollection->addWellPaths( newWellPaths );
wellPathCollection->uiCapability()->updateConnectedEditors();
newModeledWellPath->geometryDefinition()->enableTargetPointPicking( true );

View File

@ -116,8 +116,7 @@ RimModeledWellPath* RicNewWellPathLateralAtDepthFeature::createLateralAtMeasured
newModeledWellPath->createWellPathGeometry();
bool importGrouped = false;
wellPathColl->addWellPath( newModeledWellPath, importGrouped );
wellPathColl->addWellPath( newModeledWellPath );
wellPathColl->updateAllRequiredEditors();
project->scheduleCreateDisplayModelAndRedrawAllViews();

View File

@ -155,7 +155,7 @@ RimModeledWellPath* RicPasteModeledWellPathFeature::duplicateAndInitializeWellPa
QString name = sourceWellPath->name() + "(copy)";
destinationWellPath->setName( name );
wpc->addWellPath( destinationWellPath, false );
wpc->addWellPath( destinationWellPath );
// Resolve references, will connect to the fracture template
destinationWellPath->resolveReferencesRecursively();

View File

@ -149,8 +149,8 @@ void RimWellPathCollection::loadDataAndUpdate()
{
progress.setProgressDescription( QString( "Reading file %1" ).arg( wellPath->name() ) );
RimFileWellPath* fWPath = dynamic_cast<RimFileWellPath*>( wellPath );
RimModeledWellPath* mWPath = dynamic_cast<RimModeledWellPath*>( wellPath );
auto* fWPath = dynamic_cast<RimFileWellPath*>( wellPath );
auto* mWPath = dynamic_cast<RimModeledWellPath*>( wellPath );
if ( fWPath )
{
if ( !fWPath->filePath().isEmpty() )
@ -201,19 +201,19 @@ void RimWellPathCollection::loadDataAndUpdate()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimWellPath*> RimWellPathCollection::addWellPaths( QStringList filePaths, bool importGrouped, QStringList* errorMessages )
std::vector<RimWellPath*> RimWellPathCollection::addWellPaths( QStringList filePaths, QStringList* errorMessages )
{
CAF_ASSERT( errorMessages );
std::vector<RimFileWellPath*> wellPathArray;
for ( QString filePath : filePaths )
for ( const QString& filePath : filePaths )
{
// Check if this file is already open
bool alreadyOpen = false;
for ( auto wellPath : m_wellPaths )
for ( const auto& wellPath : m_wellPaths )
{
RimFileWellPath* fWPath = dynamic_cast<RimFileWellPath*>( wellPath.p() );
auto* fWPath = dynamic_cast<RimFileWellPath*>( wellPath.p() );
if ( !fWPath ) continue;
QFile f1;
@ -238,7 +238,7 @@ std::vector<RimWellPath*> RimWellPathCollection::addWellPaths( QStringList fileP
if ( fi.suffix().compare( "json" ) == 0 )
{
RimFileWellPath* wellPath = new RimFileWellPath();
auto* wellPath = new RimFileWellPath();
wellPath->setFilepath( filePath );
wellPathArray.push_back( wellPath );
}
@ -248,7 +248,7 @@ std::vector<RimWellPath*> RimWellPathCollection::addWellPaths( QStringList fileP
size_t wellPathCount = m_wellPathImporter->wellDataCount( filePath );
for ( size_t i = 0; i < wellPathCount; ++i )
{
RimFileWellPath* wellPath = new RimFileWellPath();
auto* wellPath = new RimFileWellPath();
wellPath->setFilepath( filePath );
wellPath->setWellPathIndexInFile( static_cast<int>( i ) );
wellPathArray.push_back( wellPath );
@ -257,7 +257,7 @@ std::vector<RimWellPath*> RimWellPathCollection::addWellPaths( QStringList fileP
}
}
readAndAddWellPaths( wellPathArray, importGrouped );
readAndAddWellPaths( wellPathArray );
CAF_ASSERT( wellPathArray.empty() );
scheduleRedrawAffectedViews();
@ -269,7 +269,7 @@ std::vector<RimWellPath*> RimWellPathCollection::addWellPaths( QStringList fileP
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPathCollection::addWellPath( gsl::not_null<RimWellPath*> wellPath, bool importGrouped )
void RimWellPathCollection::addWellPath( gsl::not_null<RimWellPath*> wellPath )
{
m_wellPaths.push_back( wellPath );
@ -289,11 +289,10 @@ std::vector<RimWellPath*> RimWellPathCollection::allWellPaths() const
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPathCollection::readAndAddWellPaths( std::vector<RimFileWellPath*>& wellPathArray, bool importGrouped )
void RimWellPathCollection::readAndAddWellPaths( std::vector<RimFileWellPath*>& wellPathArray )
{
caf::ProgressInfo progress( wellPathArray.size(), "Reading well paths from file" );
std::vector<RimWellPath*> wellPathsToGroup;
for ( RimFileWellPath* wellPath : wellPathArray )
{
wellPath->readWellPathFile( nullptr, m_wellPathImporter.get(), true );
@ -321,16 +320,14 @@ void RimWellPathCollection::readAndAddWellPaths( std::vector<RimFileWellPath*>&
{
wellPath->setWellPathColor( RiaColorTables::wellPathsPaletteColors().cycledColor3f( m_wellPaths.size() ) );
wellPath->setUnitSystem( findUnitSystemForWellPath( wellPath ) );
addWellPath( wellPath, false );
wellPathsToGroup.push_back( wellPath );
addWellPath( wellPath );
}
progress.incrementProgress();
}
wellPathArray.clear(); // This should not be used again. We may have deleted items
groupWellPaths( wellPathsToGroup );
groupWellPaths( allWellPaths() );
sortWellsByName();
rebuildWellPathNodes();
}
@ -338,14 +335,14 @@ void RimWellPathCollection::readAndAddWellPaths( std::vector<RimFileWellPath*>&
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPathCollection::addWellPaths( const std::vector<RimWellPath*> incomingWellPaths, bool importGrouped )
void RimWellPathCollection::addWellPaths( const std::vector<RimWellPath*> incomingWellPaths )
{
for ( const auto& wellPath : incomingWellPaths )
{
addWellPath( wellPath, importGrouped );
addWellPath( wellPath );
}
groupWellPaths( incomingWellPaths );
groupWellPaths( allWellPaths() );
sortWellsByName();
rebuildWellPathNodes();
@ -375,7 +372,7 @@ std::vector<RimWellLogFile*> RimWellPathCollection::addWellLogs( const QStringLi
if ( !wellPath )
{
wellPath = new RimWellPath();
addWellPath( wellPath, false );
addWellPath( wellPath );
}
wellPath->addWellLogFile( logFileInfo );
@ -400,30 +397,30 @@ void RimWellPathCollection::addWellPathFormations( const QStringList& filePaths
bool fileReadSuccess = false;
for ( QString filePath : filePaths )
for ( const QString& filePath : filePaths )
{
std::map<QString, cvf::ref<RigWellPathFormations>> newFormations =
m_wellPathFormationsImporter->readWellPathFormationsFromPath( filePath );
for ( auto it = newFormations.begin(); it != newFormations.end(); it++ )
for ( const auto& newFormation : newFormations )
{
fileReadSuccess = true;
RimWellPath* wellPath = tryFindMatchingWellPath( it->first );
RimWellPath* wellPath = tryFindMatchingWellPath( newFormation.first );
if ( !wellPath )
{
wellPath = new RimWellPath();
wellPath->setName( it->first );
addWellPath( wellPath, false );
wellPath->setName( newFormation.first );
addWellPath( wellPath );
RiaLogging::info( QString( "Created new well: %1" ).arg( wellPath->name() ) );
}
wellPath->setFormationsGeometry( it->second );
wellPath->setFormationsGeometry( newFormation.second );
QString wellFormationsCount = QString( "%1" ).arg( it->second->formationNamesCount() );
QString wellFormationsCount = QString( "%1" ).arg( newFormation.second->formationNamesCount() );
m_mostRecentlyUpdatedWellPath = wellPath;
outputMessage += it->first + "\t\t";
outputMessage += newFormation.first + "\t\t";
outputMessage += wellPath->name() + " \t\t\t";
outputMessage += wellFormationsCount + "\n";
}
@ -498,7 +495,7 @@ void RimWellPathCollection::scheduleRedrawAffectedViews()
//--------------------------------------------------------------------------------------------------
bool RimWellPathCollection::anyWellsContainingPerforationIntervals() const
{
for ( auto wellPath : m_wellPaths )
for ( const auto& wellPath : m_wellPaths )
{
if ( !wellPath->perforationIntervalCollection()->perforations().empty() ) return true;
}
@ -511,7 +508,7 @@ bool RimWellPathCollection::anyWellsContainingPerforationIntervals() const
size_t RimWellPathCollection::modelledWellPathCount() const
{
size_t count = 0;
for ( auto wellPath : m_wellPaths )
for ( const auto& wellPath : m_wellPaths )
{
if ( dynamic_cast<const RimModeledWellPath*>( wellPath.p() ) )
{
@ -526,7 +523,7 @@ size_t RimWellPathCollection::modelledWellPathCount() const
//--------------------------------------------------------------------------------------------------
RimWellPath* RimWellPathCollection::wellPathByName( const QString& wellPathName ) const
{
for ( auto wellPath : m_wellPaths )
for ( const auto& wellPath : m_wellPaths )
{
if ( wellPath->name() == wellPathName )
{
@ -574,56 +571,77 @@ void RimWellPathCollection::deleteWell( RimWellPath* wellPath )
//--------------------------------------------------------------------------------------------------
void RimWellPathCollection::groupWellPaths( const std::vector<RimWellPath*>& wellPaths )
{
auto rootWells = wellPathsForWellNameStem( wellPaths );
const auto& rootWells = wellPathsForWellNameStem( wellPaths );
for ( auto [groupName, wellPathCommonName] : rootWells )
for ( const auto& [groupName, wellPathsInGroup] : rootWells )
{
if ( groupName == unGroupedText() ) continue;
for ( auto wellPath : wellPathCommonName )
for ( const auto& wellPathToConnect : wellPathsInGroup )
{
// Assign the group names as well name for export
wellPath->completionSettings()->setWellNameForExport( groupName );
wellPathToConnect->completionSettings()->setWellNameForExport( groupName );
auto wellPathGeometry = wellPath->wellPathGeometry();
const auto wellPathGeometry = wellPathToConnect->wellPathGeometry();
if ( wellPathGeometry )
{
const double eps = 1.0e-2;
std::map<RimWellPath*, double> wellPathsWithCommonGeometry;
std::map<RimWellPath*, double> sharedWellPathLengths;
for ( auto existingWellPath : wellPathCommonName )
for ( const auto& otherWellPath : wellPathsInGroup )
{
if ( existingWellPath == wellPath ) continue;
if ( otherWellPath == wellPathToConnect ) continue;
if ( wellPath->name() < existingWellPath->name() ) continue;
double identicalTubeLength = existingWellPath->wellPathGeometry()->identicalTubeLength( *wellPathGeometry );
if ( identicalTubeLength > eps )
if ( otherWellPath && otherWellPath->wellPathGeometry() )
{
wellPathsWithCommonGeometry[existingWellPath] = identicalTubeLength;
const double sharedWellPathLength = otherWellPath->wellPathGeometry()->identicalTubeLength( *wellPathGeometry );
const double eps = 1.0e-2;
if ( sharedWellPathLength > eps )
{
sharedWellPathLengths[otherWellPath] = sharedWellPathLength;
}
}
}
RimWellPath* mostSimilarWellPath = nullptr;
double longestIdenticalTubeLength = 0.0;
for ( auto [existingWellPath, identicalTubeLength] : wellPathsWithCommonGeometry )
RimWellPath* longestSharedWellPath = nullptr;
double longestSharedWellPathLength = 0.0;
for ( const auto& [wellPathCandidate, sharedWellPathLength] : sharedWellPathLengths )
{
if ( existingWellPath && ( existingWellPath != wellPath ) && identicalTubeLength > longestIdenticalTubeLength )
if ( wellPathCandidate )
{
mostSimilarWellPath = existingWellPath;
longestIdenticalTubeLength = identicalTubeLength;
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 ( mostSimilarWellPath )
if ( longestSharedWellPath )
{
if ( wellPath->name() > mostSimilarWellPath->name() )
if ( wellPathToConnect->name() > longestSharedWellPath->name() )
{
wellPath->connectWellPaths( mostSimilarWellPath, longestIdenticalTubeLength );
wellPathToConnect->connectWellPaths( longestSharedWellPath, longestSharedWellPathLength );
}
else
{
mostSimilarWellPath->connectWellPaths( wellPath, longestIdenticalTubeLength );
longestSharedWellPath->connectWellPaths( wellPathToConnect, longestSharedWellPathLength );
}
}
}
@ -666,7 +684,7 @@ void RimWellPathCollection::reloadAllWellPathFormations()
{
caf::ProgressInfo progress( m_wellPaths.size(), "Reloading well picks from file" );
for ( auto wellPath : m_wellPaths )
for ( const auto& wellPath : m_wellPaths )
{
QString errorMessage;
if ( !wellPath->reloadWellPathFormationsFile( &errorMessage, m_wellPathFormationsImporter.get() ) )
@ -723,10 +741,10 @@ bool lessWellPath( const caf::PdmPointer<RimWellPath>& w1, const caf::PdmPointer
collator.setNumericMode( true );
return collator.compare( w1->name(), w2->name() ) < 0;
}
else if ( w1.notNull() )
return true;
else
return false;
if ( w1.notNull() ) return true;
return false;
}
//--------------------------------------------------------------------------------------------------

View File

@ -93,7 +93,7 @@ public:
caf::PdmField<int> wellPathClipZDistance;
void loadDataAndUpdate();
std::vector<RimWellPath*> addWellPaths( QStringList filePaths, bool importGrouped, QStringList* errorMessages );
std::vector<RimWellPath*> addWellPaths( QStringList filePaths, QStringList* errorMessages );
std::vector<RimWellPath*> allWellPaths() const;
void removeWellPath( gsl::not_null<RimWellPath*> wellPath );
@ -107,13 +107,12 @@ public:
RimWellPath* mostRecentlyUpdatedWellPath();
void readWellPathFormationFiles();
void reloadAllWellPathFormations();
void readWellPathFormationFiles();
void reloadAllWellPathFormations();
RimWellPath* wellPathByName( const QString& wellPathName ) const;
RimWellPath* tryFindMatchingWellPath( const QString& wellName ) const;
void addWellPaths( const std::vector<RimWellPath*> incomingWellPaths, bool importGrouped );
void addWellPath( gsl::not_null<RimWellPath*> wellPath, bool importGrouped );
void addWellPaths( const std::vector<RimWellPath*> incomingWellPaths );
void addWellPath( gsl::not_null<RimWellPath*> wellPath );
std::vector<RimWellLogFile*> addWellLogs( const QStringList& filePaths, QStringList* errorMessages );
void addWellPathFormations( const QStringList& filePaths );
@ -139,7 +138,7 @@ private:
caf::PdmFieldHandle* objectToggleField() override;
void readAndAddWellPaths( std::vector<RimFileWellPath*>& wellPathArray, bool importGrouped );
void readAndAddWellPaths( std::vector<RimFileWellPath*>& wellPathArray );
void sortWellsByName();
caf::AppEnum<RiaDefines::EclipseUnitSystem> findUnitSystemForWellPath( const RimWellPath* wellPath );