mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
1965 lines
94 KiB
C++
1965 lines
94 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2018 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 "RicWellPathExportMswCompletionsImpl.h"
|
|
|
|
#include "RiaLogging.h"
|
|
#include "RiaWeightedMeanCalculator.h"
|
|
|
|
#include "RicExportCompletionDataSettingsUi.h"
|
|
#include "RicExportFractureCompletionsImpl.h"
|
|
#include "RicMswExportInfo.h"
|
|
#include "RicMswTableFormatterTools.h"
|
|
#include "RicMswValveAccumulators.h"
|
|
#include "RicWellPathExportCompletionsFileTools.h"
|
|
|
|
#include "RifTextDataTableFormatter.h"
|
|
|
|
#include "RigActiveCellInfo.h"
|
|
#include "RigEclipseCaseData.h"
|
|
#include "RigGridBase.h"
|
|
#include "RigMainGrid.h"
|
|
#include "RigWellLogExtractor.h"
|
|
#include "RigWellPath.h"
|
|
#include "RigWellPathIntersectionTools.h"
|
|
|
|
#include "RimEclipseCase.h"
|
|
#include "RimFishbones.h"
|
|
#include "RimFishbonesCollection.h"
|
|
#include "RimFractureTemplate.h"
|
|
#include "RimModeledWellPath.h"
|
|
#include "RimMswCompletionParameters.h"
|
|
#include "RimPerforationCollection.h"
|
|
#include "RimPerforationInterval.h"
|
|
#include "RimProject.h"
|
|
#include "RimWellPath.h"
|
|
#include "RimWellPathCompletions.h"
|
|
#include "RimWellPathFracture.h"
|
|
#include "RimWellPathFractureCollection.h"
|
|
#include "RimWellPathTieIn.h"
|
|
#include "RimWellPathValve.h"
|
|
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
|
|
#include <algorithm>
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForAllCompletions(
|
|
const RicExportCompletionDataSettingsUi& exportSettings,
|
|
const std::vector<RimWellPath*>& wellPaths )
|
|
{
|
|
std::shared_ptr<QFile> unifiedExportFile;
|
|
std::shared_ptr<QFile> unifiedLgrExportFile;
|
|
if ( exportSettings.fileSplit() == RicExportCompletionDataSettingsUi::ExportSplit::UNIFIED_FILE )
|
|
{
|
|
{
|
|
QString fileName;
|
|
QFileInfo fi( exportSettings.customFileName() );
|
|
if ( !exportSettings.customFileName().isEmpty() )
|
|
fileName = fi.baseName() + "_MSW";
|
|
else
|
|
fileName = QString( "UnifiedCompletions_MSW_%1" ).arg( exportSettings.caseToApply->caseUserDescription() );
|
|
|
|
unifiedExportFile =
|
|
RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, fileName, fi.suffix() );
|
|
}
|
|
|
|
{
|
|
QString lgrFileName;
|
|
QFileInfo fi( exportSettings.customFileName() );
|
|
if ( !exportSettings.customFileName().isEmpty() )
|
|
lgrFileName = fi.baseName() + "_LGR_MSW";
|
|
else
|
|
lgrFileName =
|
|
QString( "UnifiedCompletions_LGR_MSW_%1" ).arg( exportSettings.caseToApply->caseUserDescription() );
|
|
|
|
unifiedLgrExportFile =
|
|
RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, lgrFileName, fi.suffix() );
|
|
}
|
|
}
|
|
|
|
for ( const auto& wellPath : wellPaths )
|
|
{
|
|
std::shared_ptr<QFile> unifiedWellPathFile;
|
|
std::shared_ptr<QFile> unifiedLgrWellPathFile;
|
|
|
|
auto allCompletions = wellPath->allCompletionsRecursively();
|
|
|
|
bool exportFractures = exportSettings.includeFractures() &&
|
|
std::any_of( allCompletions.begin(), allCompletions.end(), []( auto completion ) {
|
|
return completion->isEnabled() &&
|
|
completion->componentType() == RiaDefines::WellPathComponentType::FRACTURE;
|
|
} );
|
|
|
|
bool exportFishbones = exportSettings.includeFishbones() &&
|
|
std::any_of( allCompletions.begin(), allCompletions.end(), []( auto completion ) {
|
|
return completion->isEnabled() &&
|
|
completion->componentType() == RiaDefines::WellPathComponentType::FISHBONES;
|
|
} );
|
|
|
|
if ( exportSettings.fileSplit() == RicExportCompletionDataSettingsUi::ExportSplit::SPLIT_ON_WELL &&
|
|
!unifiedWellPathFile )
|
|
{
|
|
QString wellFileName = QString( "%1_UnifiedCompletions_MSW_%2" )
|
|
.arg( wellPath->name(), exportSettings.caseToApply->caseUserDescription() );
|
|
unifiedWellPathFile =
|
|
RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, wellFileName );
|
|
unifiedLgrWellPathFile =
|
|
RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, wellFileName + "_LGR" );
|
|
}
|
|
|
|
{
|
|
// Always use perforation functions to export well segments along well path.
|
|
// If no perforations are present, skip Perforation from file name
|
|
|
|
std::shared_ptr<QFile> exportFile;
|
|
std::shared_ptr<QFile> lgrExportFile;
|
|
|
|
if ( unifiedExportFile )
|
|
{
|
|
exportFile = unifiedExportFile;
|
|
lgrExportFile = unifiedLgrExportFile;
|
|
}
|
|
else if ( unifiedWellPathFile )
|
|
{
|
|
exportFile = unifiedWellPathFile;
|
|
lgrExportFile = unifiedLgrWellPathFile;
|
|
}
|
|
else
|
|
{
|
|
bool anyPerforationsPresent =
|
|
exportSettings.includeFractures() &&
|
|
std::any_of( allCompletions.begin(), allCompletions.end(), []( auto completion ) {
|
|
return completion->isEnabled() &&
|
|
completion->componentType() == RiaDefines::WellPathComponentType::PERFORATION_INTERVAL;
|
|
} );
|
|
|
|
QString perforationText = anyPerforationsPresent ? "Perforation_" : "";
|
|
QString fileName =
|
|
QString( "%1_%2MSW_%3" )
|
|
.arg( wellPath->name(), perforationText, exportSettings.caseToApply->caseUserDescription() );
|
|
exportFile = RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, fileName );
|
|
lgrExportFile =
|
|
RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, fileName + "_LGR" );
|
|
}
|
|
exportWellSegmentsForPerforations( exportSettings.caseToApply,
|
|
exportFile,
|
|
lgrExportFile,
|
|
wellPath,
|
|
exportSettings.timeStep,
|
|
exportSettings.exportDataSourceAsComment(),
|
|
exportSettings.exportCompletionWelspecAfterMainBore() );
|
|
}
|
|
|
|
if ( exportFractures )
|
|
{
|
|
std::shared_ptr<QFile> exportFile;
|
|
std::shared_ptr<QFile> lgrExportFile;
|
|
|
|
if ( unifiedExportFile )
|
|
{
|
|
exportFile = unifiedExportFile;
|
|
lgrExportFile = unifiedLgrExportFile;
|
|
}
|
|
else if ( unifiedWellPathFile )
|
|
{
|
|
exportFile = unifiedWellPathFile;
|
|
lgrExportFile = unifiedLgrWellPathFile;
|
|
}
|
|
else
|
|
{
|
|
QString fileName =
|
|
QString( "%1_Fracture_MSW_%2" ).arg( wellPath->name(), exportSettings.caseToApply->caseUserDescription() );
|
|
exportFile = RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, fileName );
|
|
lgrExportFile =
|
|
RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, fileName + "_LGR" );
|
|
}
|
|
exportWellSegmentsForFractures( exportSettings.caseToApply,
|
|
exportFile,
|
|
lgrExportFile,
|
|
wellPath,
|
|
exportSettings.exportDataSourceAsComment(),
|
|
exportSettings.exportCompletionWelspecAfterMainBore() );
|
|
}
|
|
|
|
if ( exportFishbones )
|
|
{
|
|
std::shared_ptr<QFile> exportFile;
|
|
std::shared_ptr<QFile> lgrExportFile;
|
|
|
|
if ( unifiedExportFile )
|
|
{
|
|
exportFile = unifiedExportFile;
|
|
lgrExportFile = unifiedLgrExportFile;
|
|
}
|
|
else if ( unifiedWellPathFile )
|
|
{
|
|
exportFile = unifiedWellPathFile;
|
|
lgrExportFile = unifiedLgrWellPathFile;
|
|
}
|
|
else
|
|
{
|
|
QString fileName =
|
|
QString( "%1_Fishbones_MSW_%2" ).arg( wellPath->name(), exportSettings.caseToApply->caseUserDescription() );
|
|
exportFile = RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, fileName );
|
|
lgrExportFile =
|
|
RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, fileName + "_LGR" );
|
|
}
|
|
exportWellSegmentsForFishbones( exportSettings.caseToApply,
|
|
exportFile,
|
|
lgrExportFile,
|
|
wellPath,
|
|
exportSettings.exportDataSourceAsComment(),
|
|
exportSettings.exportCompletionWelspecAfterMainBore() );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForPerforations( RimEclipseCase* eclipseCase,
|
|
std::shared_ptr<QFile> exportFile,
|
|
std::shared_ptr<QFile> lgrExportFile,
|
|
const RimWellPath* wellPath,
|
|
int timeStep,
|
|
bool exportDataSourceAsComment,
|
|
bool completionSegmentsAfterMainBore )
|
|
{
|
|
RiaDefines::EclipseUnitSystem unitSystem = eclipseCase->eclipseCaseData()->unitsType();
|
|
|
|
auto mswParameters = wellPath->mswCompletionParameters();
|
|
|
|
if ( !mswParameters ) return;
|
|
|
|
double initialMD = 0.0; // Start measured depth location to export MSW data for. Either based on first
|
|
// intersection with active grid, or user defined value.
|
|
|
|
auto cellIntersections = generateCellSegments( eclipseCase, wellPath, mswParameters, &initialMD );
|
|
|
|
RicMswExportInfo exportInfo( wellPath,
|
|
unitSystem,
|
|
initialMD,
|
|
mswParameters->lengthAndDepth().text(),
|
|
mswParameters->pressureDrop().text() );
|
|
|
|
if ( generatePerforationsMswExportInfo( eclipseCase,
|
|
wellPath,
|
|
timeStep,
|
|
initialMD,
|
|
cellIntersections,
|
|
&exportInfo,
|
|
exportInfo.mainBoreBranch() ) )
|
|
{
|
|
int branchNumber = 1;
|
|
|
|
assignBranchNumbersToBranch( eclipseCase, &exportInfo, exportInfo.mainBoreBranch(), &branchNumber );
|
|
|
|
double maxSegmentLength = mswParameters->maxSegmentLength();
|
|
|
|
{
|
|
QTextStream stream( exportFile.get() );
|
|
RifTextDataTableFormatter formatter( stream );
|
|
|
|
RicMswTableFormatterTools::generateWelsegsTable( formatter,
|
|
exportInfo,
|
|
maxSegmentLength,
|
|
completionSegmentsAfterMainBore );
|
|
bool exportLgrData = false;
|
|
RicMswTableFormatterTools::generateCompsegTables( formatter, exportInfo, exportLgrData );
|
|
RicMswTableFormatterTools::generateWsegvalvTable( formatter, exportInfo );
|
|
RicMswTableFormatterTools::generateWsegAicdTable( formatter, exportInfo );
|
|
}
|
|
|
|
if ( exportInfo.hasSubGridIntersections() )
|
|
{
|
|
QTextStream stream( lgrExportFile.get() );
|
|
RifTextDataTableFormatter formatter( stream );
|
|
|
|
RicMswTableFormatterTools::generateWelsegsTable( formatter,
|
|
exportInfo,
|
|
maxSegmentLength,
|
|
completionSegmentsAfterMainBore );
|
|
bool exportLgrData = true;
|
|
RicMswTableFormatterTools::generateCompsegTables( formatter, exportInfo, exportLgrData );
|
|
RicMswTableFormatterTools::generateWsegvalvTable( formatter, exportInfo );
|
|
RicMswTableFormatterTools::generateWsegAicdTable( formatter, exportInfo );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForFractures( RimEclipseCase* eclipseCase,
|
|
std::shared_ptr<QFile> exportFile,
|
|
std::shared_ptr<QFile> lgrExportFile,
|
|
const RimWellPath* wellPath,
|
|
bool exportDataSourceAsComment,
|
|
bool completionSegmentsAfterMainBore )
|
|
{
|
|
RiaDefines::EclipseUnitSystem unitSystem = eclipseCase->eclipseCaseData()->unitsType();
|
|
|
|
if ( eclipseCase == nullptr )
|
|
{
|
|
RiaLogging::error(
|
|
"Export Fracture Well Segments: Cannot export completions data without specified eclipse case" );
|
|
return;
|
|
}
|
|
|
|
auto mswParameters = wellPath->mswCompletionParameters();
|
|
|
|
if ( !mswParameters ) return;
|
|
|
|
double initialMD = 0.0; // Start measured depth location to export MSW data for. Either based on first
|
|
// intersection with active grid, or user defined value.
|
|
|
|
auto cellIntersections = generateCellSegments( eclipseCase, wellPath, mswParameters, &initialMD );
|
|
|
|
RicMswExportInfo exportInfo( wellPath,
|
|
unitSystem,
|
|
initialMD,
|
|
mswParameters->lengthAndDepth().text(),
|
|
mswParameters->pressureDrop().text() );
|
|
|
|
generateFracturesMswExportInfo( eclipseCase,
|
|
wellPath,
|
|
initialMD,
|
|
cellIntersections,
|
|
&exportInfo,
|
|
exportInfo.mainBoreBranch() );
|
|
|
|
int branchNumber = 1;
|
|
assignBranchNumbersToBranch( eclipseCase, &exportInfo, exportInfo.mainBoreBranch(), &branchNumber );
|
|
|
|
auto doExport = []( RifTextDataTableFormatter& formatter,
|
|
bool exportDataSourceAsComment,
|
|
RicMswExportInfo& exportInfo,
|
|
const RimWellPath* wellPath,
|
|
bool completionSegmentsAfterMainBore,
|
|
bool exportLgrData ) {
|
|
formatter.setOptionalComment( exportDataSourceAsComment );
|
|
|
|
double maxSegmentLength = wellPath->mswCompletionParameters()->maxSegmentLength();
|
|
|
|
RicMswTableFormatterTools::generateWelsegsTable( formatter,
|
|
exportInfo,
|
|
maxSegmentLength,
|
|
completionSegmentsAfterMainBore );
|
|
|
|
RicMswTableFormatterTools::generateCompsegTables( formatter, exportInfo, exportLgrData );
|
|
};
|
|
|
|
{
|
|
QTextStream stream( exportFile.get() );
|
|
RifTextDataTableFormatter formatter( stream );
|
|
bool exportLgrData = false;
|
|
|
|
doExport( formatter, exportDataSourceAsComment, exportInfo, wellPath, completionSegmentsAfterMainBore, exportLgrData );
|
|
}
|
|
|
|
if ( exportInfo.hasSubGridIntersections() )
|
|
{
|
|
QTextStream stream( lgrExportFile.get() );
|
|
RifTextDataTableFormatter formatter( stream );
|
|
|
|
bool exportLgrData = true;
|
|
doExport( formatter, exportDataSourceAsComment, exportInfo, wellPath, completionSegmentsAfterMainBore, exportLgrData );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForFishbones( RimEclipseCase* eclipseCase,
|
|
std::shared_ptr<QFile> exportFile,
|
|
std::shared_ptr<QFile> lgrExportFile,
|
|
const RimWellPath* wellPath,
|
|
bool exportDataSourceAsComment,
|
|
bool completionSegmentsAfterMainBore )
|
|
{
|
|
auto fishbonesSubs = wellPath->fishbonesCollection()->activeFishbonesSubs();
|
|
|
|
if ( eclipseCase == nullptr )
|
|
{
|
|
RiaLogging::error( "Export Well Segments: Cannot export completions data without specified eclipse case" );
|
|
return;
|
|
}
|
|
|
|
double initialMD = 0.0; // Start measured depth location to export MSW data for. Either based on first
|
|
// intersection with active grid, or user defined value.
|
|
|
|
auto mswParameters = wellPath->mswCompletionParameters();
|
|
auto cellIntersections = generateCellSegments( eclipseCase, wellPath, mswParameters, &initialMD );
|
|
|
|
RiaDefines::EclipseUnitSystem unitSystem = eclipseCase->eclipseCaseData()->unitsType();
|
|
|
|
RicMswExportInfo exportInfo( wellPath,
|
|
unitSystem,
|
|
initialMD,
|
|
mswParameters->lengthAndDepth().text(),
|
|
mswParameters->pressureDrop().text() );
|
|
|
|
generateFishbonesMswExportInfo( eclipseCase,
|
|
wellPath,
|
|
initialMD,
|
|
cellIntersections,
|
|
fishbonesSubs,
|
|
true,
|
|
&exportInfo,
|
|
exportInfo.mainBoreBranch() );
|
|
|
|
updateDataForMultipleItemsInSameGridCell( exportInfo.mainBoreBranch() );
|
|
|
|
int branchNumber = 1;
|
|
|
|
assignBranchNumbersToBranch( eclipseCase, &exportInfo, exportInfo.mainBoreBranch(), &branchNumber );
|
|
|
|
double maxSegmentLength = wellPath->mswCompletionParameters()->maxSegmentLength();
|
|
|
|
{
|
|
QTextStream stream( exportFile.get() );
|
|
RifTextDataTableFormatter formatter( stream );
|
|
formatter.setOptionalComment( exportDataSourceAsComment );
|
|
|
|
RicMswTableFormatterTools::generateWelsegsTable( formatter,
|
|
exportInfo,
|
|
maxSegmentLength,
|
|
completionSegmentsAfterMainBore );
|
|
bool exportLgrData = false;
|
|
RicMswTableFormatterTools::generateCompsegTables( formatter, exportInfo, exportLgrData );
|
|
RicMswTableFormatterTools::generateWsegvalvTable( formatter, exportInfo );
|
|
}
|
|
|
|
if ( exportInfo.hasSubGridIntersections() )
|
|
{
|
|
QTextStream stream( lgrExportFile.get() );
|
|
RifTextDataTableFormatter formatter( stream );
|
|
formatter.setOptionalComment( exportDataSourceAsComment );
|
|
|
|
RicMswTableFormatterTools::generateWelsegsTable( formatter,
|
|
exportInfo,
|
|
maxSegmentLength,
|
|
completionSegmentsAfterMainBore );
|
|
|
|
bool exportLgr = true;
|
|
RicMswTableFormatterTools::generateCompsegTables( formatter, exportInfo, exportLgr );
|
|
RicMswTableFormatterTools::generateWsegvalvTable( formatter, exportInfo );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::updateDataForMultipleItemsInSameGridCell( gsl::not_null<RicMswBranch*> branch )
|
|
{
|
|
{
|
|
// Update effective diameter
|
|
|
|
std::map<size_t, std::set<RicMswSegment*>> segmentsInCell;
|
|
{
|
|
auto segments = branch->allSegmentsRecursively();
|
|
for ( auto s : segments )
|
|
{
|
|
auto cellsIntersected = s->globalCellsIntersected();
|
|
if ( !cellsIntersected.empty() )
|
|
{
|
|
for ( auto index : cellsIntersected )
|
|
{
|
|
segmentsInCell[index].insert( s );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( auto [index, segmentsInSameCell] : segmentsInCell )
|
|
{
|
|
// Compute effective diameter based on square root of the sum of diameter squared
|
|
// Deff = sqrt(d1^2 + d2^2 + ..)
|
|
double effectiveDiameter = 0.0;
|
|
|
|
for ( auto seg : segmentsInSameCell )
|
|
{
|
|
effectiveDiameter += ( seg->equivalentDiameter() * seg->equivalentDiameter() );
|
|
}
|
|
|
|
effectiveDiameter = sqrt( effectiveDiameter );
|
|
|
|
for ( auto seg : segmentsInSameCell )
|
|
{
|
|
seg->setEffectiveDiameter( effectiveDiameter );
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
// Update IDC area
|
|
|
|
std::map<size_t, std::set<RicMswFishbonesICD*>> icdsInCell;
|
|
|
|
{
|
|
auto segments = branch->allSegmentsRecursively();
|
|
for ( auto s : segments )
|
|
{
|
|
for ( auto completion : s->completions() )
|
|
{
|
|
if ( auto icd = dynamic_cast<RicMswFishbonesICD*>( completion ) )
|
|
{
|
|
for ( auto icdSegment : icd->segments() )
|
|
{
|
|
for ( auto gridIntersection : icdSegment->intersections() )
|
|
{
|
|
icdsInCell[gridIntersection->globalCellIndex()].insert( icd );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( auto [Index, icdsInSameCell] : icdsInCell )
|
|
{
|
|
// Compute area sum for all ICDs in same grid cell
|
|
double areaSum = 0.0;
|
|
|
|
for ( auto icd : icdsInSameCell )
|
|
{
|
|
areaSum += icd->area();
|
|
}
|
|
|
|
for ( auto icd : icdsInSameCell )
|
|
{
|
|
icd->setArea( areaSum );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::generateFishbonesMswExportInfo(
|
|
const RimEclipseCase* eclipseCase,
|
|
const RimWellPath* wellPath,
|
|
double initialMD,
|
|
const std::vector<WellPathCellIntersectionInfo>& cellIntersections,
|
|
bool enableSegmentSplitting,
|
|
gsl::not_null<RicMswExportInfo*> exportInfo,
|
|
gsl::not_null<RicMswBranch*> branch )
|
|
{
|
|
std::vector<RimFishbones*> fishbonesSubs = wellPath->fishbonesCollection()->activeFishbonesSubs();
|
|
if ( fishbonesSubs.empty() ) return;
|
|
|
|
generateFishbonesMswExportInfo( eclipseCase,
|
|
wellPath,
|
|
initialMD,
|
|
cellIntersections,
|
|
fishbonesSubs,
|
|
enableSegmentSplitting,
|
|
exportInfo,
|
|
branch );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::generateFishbonesMswExportInfo(
|
|
const RimEclipseCase* eclipseCase,
|
|
const RimWellPath* wellPath,
|
|
double initialMD,
|
|
const std::vector<WellPathCellIntersectionInfo>& cellIntersections,
|
|
const std::vector<RimFishbones*>& fishbonesSubs,
|
|
bool enableSegmentSplitting,
|
|
gsl::not_null<RicMswExportInfo*> exportInfo,
|
|
gsl::not_null<RicMswBranch*> branch )
|
|
{
|
|
std::vector<WellPathCellIntersectionInfo> filteredIntersections =
|
|
filterIntersections( cellIntersections, initialMD, wellPath->wellPathGeometry(), eclipseCase );
|
|
|
|
auto mswParameters = wellPath->mswCompletionParameters();
|
|
|
|
bool foundSubGridIntersections = false;
|
|
|
|
// Create a dummy perforation interval
|
|
RimPerforationInterval perfInterval;
|
|
|
|
createWellPathSegments( branch, filteredIntersections, { &perfInterval }, wellPath, -1, eclipseCase, &foundSubGridIntersections );
|
|
|
|
double maxSegmentLength = enableSegmentSplitting ? mswParameters->maxSegmentLength()
|
|
: std::numeric_limits<double>::infinity();
|
|
|
|
double subStartMD = wellPath->fishbonesCollection()->startMD();
|
|
double subStartTVD = RicMswTableFormatterTools::tvdFromMeasuredDepth( branch->wellPath(), subStartMD );
|
|
|
|
auto unitSystem = exportInfo->unitSystem();
|
|
|
|
for ( RimFishbones* subs : fishbonesSubs )
|
|
{
|
|
std::map<size_t, std::vector<size_t>> subAndLateralIndices;
|
|
for ( const auto& [subIndex, lateralIndex] : subs->installedLateralIndices() )
|
|
{
|
|
subAndLateralIndices[subIndex].push_back( lateralIndex );
|
|
}
|
|
|
|
// Find cell intersections closest to each sub location
|
|
std::map<size_t, std::vector<size_t>> closestSubForCellIntersections;
|
|
std::map<size_t, size_t> cellIntersectionContainingSubIndex;
|
|
{
|
|
auto fishboneSectionStart = subs->startMD();
|
|
auto fishboneSectionEnd = subs->endMD();
|
|
|
|
for ( size_t intersectionIndex = 0; intersectionIndex < filteredIntersections.size(); intersectionIndex++ )
|
|
{
|
|
const auto& cellIntersection = filteredIntersections[intersectionIndex];
|
|
if ( ( fishboneSectionEnd >= cellIntersection.startMD ) &&
|
|
( fishboneSectionStart <= cellIntersection.endMD ) )
|
|
|
|
{
|
|
double intersectionMidpoint = 0.5 * ( cellIntersection.startMD + cellIntersection.endMD );
|
|
size_t closestSubIndex = 0;
|
|
double closestDistance = std::numeric_limits<double>::infinity();
|
|
for ( const auto& [subIndex, lateralIndices] : subAndLateralIndices )
|
|
{
|
|
double subMD = subs->measuredDepth( subIndex );
|
|
|
|
if ( ( cellIntersection.startMD <= subMD ) && ( subMD <= cellIntersection.endMD ) )
|
|
{
|
|
cellIntersectionContainingSubIndex[subIndex] = intersectionIndex;
|
|
}
|
|
|
|
auto distanceCandicate = std::abs( subMD - intersectionMidpoint );
|
|
if ( distanceCandicate < closestDistance )
|
|
{
|
|
closestDistance = distanceCandicate;
|
|
closestSubIndex = subIndex;
|
|
}
|
|
}
|
|
|
|
closestSubForCellIntersections[closestSubIndex].push_back( intersectionIndex );
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( const auto& [subIndex, lateralIndices] : subAndLateralIndices )
|
|
{
|
|
double subEndMD = subs->measuredDepth( subIndex );
|
|
double subEndTVD = RicMswTableFormatterTools::tvdFromMeasuredDepth( branch->wellPath(), subEndMD );
|
|
|
|
{
|
|
// Add completion for ICD
|
|
auto icdSegment =
|
|
std::make_unique<RicMswSegment>( "ICD segment", subEndMD, subEndMD + 0.1, subEndTVD, subEndTVD, subIndex );
|
|
|
|
for ( auto lateralIndex : lateralIndices )
|
|
{
|
|
QString label = QString( "Lateral %1" ).arg( lateralIndex + 1 );
|
|
icdSegment->addCompletion(
|
|
std::make_unique<RicMswFishbones>( label, wellPath, subEndMD, subEndTVD, lateralIndex ) );
|
|
}
|
|
|
|
assignFishbonesLateralIntersections( eclipseCase,
|
|
branch->wellPath(),
|
|
subs,
|
|
icdSegment.get(),
|
|
&foundSubGridIntersections,
|
|
maxSegmentLength,
|
|
unitSystem );
|
|
|
|
auto icdCompletion =
|
|
std::make_unique<RicMswFishbonesICD>( QString( "ICD" ), wellPath, subEndMD, subEndTVD, nullptr );
|
|
icdCompletion->setFlowCoefficient( subs->icdFlowCoefficient() );
|
|
double icdOrificeRadius = subs->icdOrificeDiameter( unitSystem ) / 2;
|
|
icdCompletion->setArea( icdOrificeRadius * icdOrificeRadius * cvf::PI_D * subs->icdCount() );
|
|
|
|
// assign open hole segments to sub
|
|
{
|
|
const RigMainGrid* mainGrid = eclipseCase->mainGrid();
|
|
|
|
std::set<size_t> indices;
|
|
for ( auto intersectionIndex : closestSubForCellIntersections[subIndex] )
|
|
{
|
|
indices.insert( intersectionIndex );
|
|
}
|
|
|
|
indices.insert( cellIntersectionContainingSubIndex[subIndex] );
|
|
|
|
for ( auto intersectionIndex : indices )
|
|
{
|
|
auto intersection = filteredIntersections[intersectionIndex];
|
|
if ( intersection.globCellIndex >= mainGrid->globalCellArray().size() ) continue;
|
|
|
|
size_t localGridCellIndex = 0u;
|
|
const RigGridBase* localGrid =
|
|
mainGrid->gridAndGridLocalIdxFromGlobalCellIdx( intersection.globCellIndex,
|
|
&localGridCellIndex );
|
|
QString gridName;
|
|
if ( localGrid != mainGrid )
|
|
{
|
|
gridName = QString::fromStdString( localGrid->gridName() );
|
|
foundSubGridIntersections = true;
|
|
}
|
|
|
|
size_t i, j, k;
|
|
localGrid->ijkFromCellIndex( localGridCellIndex, &i, &j, &k );
|
|
cvf::Vec3st localIJK( i, j, k );
|
|
|
|
auto mswIntersect =
|
|
std::make_shared<RicMswSegmentCellIntersection>( gridName,
|
|
intersection.globCellIndex,
|
|
localIJK,
|
|
intersection.intersectionLengthsInCellCS );
|
|
icdSegment->addIntersection( mswIntersect );
|
|
}
|
|
}
|
|
|
|
icdCompletion->addSegment( std::move( icdSegment ) );
|
|
|
|
RicMswSegment* segmentOnParentBranch = branch->findClosestSegmentWithLowerMD( subEndMD );
|
|
if ( segmentOnParentBranch )
|
|
{
|
|
segmentOnParentBranch->addCompletion( std::move( icdCompletion ) );
|
|
}
|
|
}
|
|
|
|
subStartMD = subEndMD;
|
|
subStartTVD = subEndTVD;
|
|
}
|
|
}
|
|
exportInfo->setHasSubGridIntersections( exportInfo->hasSubGridIntersections() || foundSubGridIntersections );
|
|
// branch->sortSegments();
|
|
|
|
std::vector<RimModeledWellPath*> connectedWellPaths = wellPathsWithTieIn( wellPath );
|
|
for ( auto childWellPath : connectedWellPaths )
|
|
{
|
|
auto childMswBranch = createChildMswBranch( childWellPath );
|
|
auto mswParameters = childWellPath->mswCompletionParameters();
|
|
|
|
double startOfChildMD = 0.0; // this is currently not used, as the tie-in MD is used
|
|
auto childCellIntersections = generateCellSegments( eclipseCase, childWellPath, mswParameters, &startOfChildMD );
|
|
auto initialChildMD = childWellPath->wellPathTieIn()->tieInMeasuredDepth();
|
|
|
|
generateFishbonesMswExportInfo( eclipseCase,
|
|
childWellPath,
|
|
initialChildMD,
|
|
childCellIntersections,
|
|
enableSegmentSplitting,
|
|
exportInfo,
|
|
childMswBranch.get() );
|
|
|
|
branch->addChildBranch( std::move( childMswBranch ) );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::generateFishbonesMswExportInfoForWell( const RimEclipseCase* eclipseCase,
|
|
const RimWellPath* wellPath,
|
|
gsl::not_null<RicMswExportInfo*> exportInfo,
|
|
gsl::not_null<RicMswBranch*> branch )
|
|
{
|
|
double initialMD = 0.0; // Start measured depth location to export MSW data for. Either based on first
|
|
// intersection with active grid, or user defined value.
|
|
|
|
auto mswParameters = wellPath->mswCompletionParameters();
|
|
auto cellIntersections = generateCellSegments( eclipseCase, wellPath, mswParameters, &initialMD );
|
|
|
|
RiaDefines::EclipseUnitSystem unitSystem = eclipseCase->eclipseCaseData()->unitsType();
|
|
|
|
bool enableSegmentSplitting = false;
|
|
generateFishbonesMswExportInfo( eclipseCase, wellPath, initialMD, cellIntersections, enableSegmentSplitting, exportInfo, branch );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RicWellPathExportMswCompletionsImpl::generateFracturesMswExportInfo(
|
|
RimEclipseCase* eclipseCase,
|
|
const RimWellPath* wellPath,
|
|
double initialMD,
|
|
const std::vector<WellPathCellIntersectionInfo>& cellIntersections,
|
|
gsl::not_null<RicMswExportInfo*> exportInfo,
|
|
gsl::not_null<RicMswBranch*> branch )
|
|
{
|
|
auto mswParameters = wellPath->mswCompletionParameters();
|
|
auto fractures = wellPath->fractureCollection()->activeFractures();
|
|
|
|
std::vector<WellPathCellIntersectionInfo> filteredIntersections =
|
|
filterIntersections( cellIntersections, initialMD, wellPath->wellPathGeometry(), eclipseCase );
|
|
|
|
// Create a dummy perforation interval
|
|
RimPerforationInterval perfInterval;
|
|
|
|
bool foundSubGridIntersections = false;
|
|
createWellPathSegments( branch, filteredIntersections, { &perfInterval }, wellPath, -1, eclipseCase, &foundSubGridIntersections );
|
|
|
|
// Check if fractures are to be assigned to current main bore segment
|
|
for ( RimWellPathFracture* fracture : fractures )
|
|
{
|
|
double fractureStartMD = fracture->fractureMD();
|
|
if ( fracture->fractureTemplate()->orientationType() == RimFractureTemplate::ALONG_WELL_PATH )
|
|
{
|
|
double perforationLength = fracture->fractureTemplate()->perforationLength();
|
|
fractureStartMD -= 0.5 * perforationLength;
|
|
}
|
|
|
|
auto segment = branch->findClosestSegmentWithLowerMD( fractureStartMD );
|
|
if ( segment )
|
|
{
|
|
std::vector<RigCompletionData> completionData =
|
|
RicExportFractureCompletionsImpl::generateCompdatValues( eclipseCase,
|
|
wellPath->completionSettings()->wellNameForExport(),
|
|
wellPath->wellPathGeometry(),
|
|
{ fracture },
|
|
nullptr,
|
|
nullptr );
|
|
|
|
assignFractureCompletionsToCellSegment( eclipseCase,
|
|
wellPath,
|
|
fracture,
|
|
completionData,
|
|
segment,
|
|
&foundSubGridIntersections );
|
|
}
|
|
}
|
|
|
|
exportInfo->setHasSubGridIntersections( exportInfo->hasSubGridIntersections() || foundSubGridIntersections );
|
|
branch->sortSegments();
|
|
|
|
std::vector<RimModeledWellPath*> connectedWellPaths = wellPathsWithTieIn( wellPath );
|
|
for ( auto childWellPath : connectedWellPaths )
|
|
{
|
|
auto childMswBranch = createChildMswBranch( childWellPath );
|
|
auto mswParameters = childWellPath->mswCompletionParameters();
|
|
|
|
double startOfChildMD = 0.0; // this is currently not used, as the tie-in MD is used
|
|
auto childCellIntersections = generateCellSegments( eclipseCase, childWellPath, mswParameters, &startOfChildMD );
|
|
auto initialChildMD = childWellPath->wellPathTieIn()->tieInMeasuredDepth();
|
|
|
|
if ( generateFracturesMswExportInfo( eclipseCase,
|
|
childWellPath,
|
|
initialChildMD,
|
|
childCellIntersections,
|
|
exportInfo,
|
|
childMswBranch.get() ) )
|
|
{
|
|
branch->addChildBranch( std::move( childMswBranch ) );
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RicWellPathExportMswCompletionsImpl::generatePerforationsMswExportInfo(
|
|
RimEclipseCase* eclipseCase,
|
|
const RimWellPath* wellPath,
|
|
int timeStep,
|
|
double initialMD,
|
|
const std::vector<WellPathCellIntersectionInfo>& cellIntersections,
|
|
gsl::not_null<RicMswExportInfo*> exportInfo,
|
|
gsl::not_null<RicMswBranch*> branch )
|
|
{
|
|
auto perforationIntervals = wellPath->perforationIntervalCollection()->activePerforations();
|
|
|
|
// Check if there exist overlap between valves in a perforation interval
|
|
for ( const auto& perfInterval : perforationIntervals )
|
|
{
|
|
for ( const auto& valve : perfInterval->valves() )
|
|
{
|
|
for ( const auto& otherValve : perfInterval->valves() )
|
|
{
|
|
if ( otherValve != valve )
|
|
{
|
|
bool hasIntersection =
|
|
!( ( valve->endMD() < otherValve->startMD() ) || ( otherValve->endMD() < valve->startMD() ) );
|
|
|
|
if ( hasIntersection )
|
|
{
|
|
RiaLogging::error(
|
|
QString( "Valve overlap detected for perforation interval : %1" ).arg( perfInterval->name() ) );
|
|
|
|
RiaLogging::error( "Name of valves" );
|
|
RiaLogging::error( valve->name() );
|
|
RiaLogging::error( otherValve->name() );
|
|
|
|
RiaLogging::error( "Failed to export well segments" );
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<WellPathCellIntersectionInfo> filteredIntersections =
|
|
filterIntersections( cellIntersections, initialMD, wellPath->wellPathGeometry(), eclipseCase );
|
|
|
|
bool foundSubGridIntersections = false;
|
|
|
|
createWellPathSegments( branch,
|
|
filteredIntersections,
|
|
perforationIntervals,
|
|
wellPath,
|
|
timeStep,
|
|
eclipseCase,
|
|
&foundSubGridIntersections );
|
|
|
|
createValveCompletions( branch, perforationIntervals, wellPath, exportInfo->unitSystem() );
|
|
|
|
const RigActiveCellInfo* activeCellInfo =
|
|
eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
|
|
|
|
assignValveContributionsToSuperICDsOrAICDs( branch,
|
|
perforationIntervals,
|
|
filteredIntersections,
|
|
activeCellInfo,
|
|
exportInfo->unitSystem() );
|
|
moveIntersectionsToICVs( branch, perforationIntervals, exportInfo->unitSystem() );
|
|
moveIntersectionsToSuperICDsOrAICDs( branch );
|
|
|
|
exportInfo->setHasSubGridIntersections( exportInfo->hasSubGridIntersections() || foundSubGridIntersections );
|
|
branch->sortSegments();
|
|
|
|
std::vector<RimModeledWellPath*> connectedWellPaths = wellPathsWithTieIn( wellPath );
|
|
|
|
for ( auto childWellPath : connectedWellPaths )
|
|
{
|
|
auto childMswBranch = createChildMswBranch( childWellPath );
|
|
auto mswParameters = childWellPath->mswCompletionParameters();
|
|
|
|
double startOfChildMD = 0.0; // this is currently not used, as the tie-in MD is used
|
|
auto childCellIntersections = generateCellSegments( eclipseCase, childWellPath, mswParameters, &startOfChildMD );
|
|
auto initialChildMD = childWellPath->wellPathTieIn()->tieInMeasuredDepth();
|
|
|
|
if ( generatePerforationsMswExportInfo( eclipseCase,
|
|
childWellPath,
|
|
timeStep,
|
|
initialChildMD,
|
|
childCellIntersections,
|
|
exportInfo,
|
|
childMswBranch.get() ) )
|
|
{
|
|
branch->addChildBranch( std::move( childMswBranch ) );
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<WellPathCellIntersectionInfo>
|
|
RicWellPathExportMswCompletionsImpl::generateCellSegments( const RimEclipseCase* eclipseCase,
|
|
const RimWellPath* wellPath,
|
|
const RimMswCompletionParameters* mswParameters,
|
|
gsl::not_null<double*> initialMD )
|
|
{
|
|
const RigActiveCellInfo* activeCellInfo =
|
|
eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
|
|
|
|
auto wellPathGeometry = wellPath->wellPathGeometry();
|
|
CVF_ASSERT( wellPathGeometry );
|
|
|
|
const std::vector<cvf::Vec3d>& coords = wellPathGeometry->uniqueWellPathPoints();
|
|
const std::vector<double>& mds = wellPathGeometry->uniqueMeasuredDepths();
|
|
CVF_ASSERT( !coords.empty() && !mds.empty() );
|
|
|
|
const RigMainGrid* mainGrid = eclipseCase->mainGrid();
|
|
|
|
std::vector<WellPathCellIntersectionInfo> allIntersections =
|
|
RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath( eclipseCase->eclipseCaseData(),
|
|
wellPath->name(),
|
|
coords,
|
|
mds );
|
|
if ( allIntersections.empty() ) return {};
|
|
|
|
std::vector<WellPathCellIntersectionInfo> continuousIntersections =
|
|
RigWellPathIntersectionTools::buildContinuousIntersections( allIntersections, mainGrid );
|
|
|
|
if ( mswParameters->referenceMDType() == RimMswCompletionParameters::ReferenceMDType::MANUAL_REFERENCE_MD )
|
|
{
|
|
*initialMD = mswParameters->manualReferenceMD();
|
|
}
|
|
else
|
|
{
|
|
for ( const WellPathCellIntersectionInfo& intersection : continuousIntersections )
|
|
{
|
|
if ( activeCellInfo->isActive( intersection.globCellIndex ) )
|
|
{
|
|
*initialMD = intersection.startMD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
double startOfFirstCompletion = std::numeric_limits<double>::infinity();
|
|
{
|
|
std::vector<const RimWellPathComponentInterface*> allCompletions = wellPath->completions()->allCompletions();
|
|
|
|
for ( const RimWellPathComponentInterface* completion : allCompletions )
|
|
{
|
|
if ( completion->isEnabled() && completion->startMD() < startOfFirstCompletion )
|
|
{
|
|
startOfFirstCompletion = completion->startMD();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initial MD is the lowest MD based on grid intersection and start of fracture completions
|
|
// https://github.com/OPM/ResInsight/issues/6071
|
|
*initialMD = std::min( *initialMD, startOfFirstCompletion );
|
|
}
|
|
|
|
return continuousIntersections;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<WellPathCellIntersectionInfo>
|
|
RicWellPathExportMswCompletionsImpl::filterIntersections( const std::vector<WellPathCellIntersectionInfo>& intersections,
|
|
double initialMD,
|
|
gsl::not_null<const RigWellPath*> wellPathGeometry,
|
|
gsl::not_null<const RimEclipseCase*> eclipseCase )
|
|
{
|
|
std::vector<WellPathCellIntersectionInfo> filteredIntersections;
|
|
|
|
if ( !intersections.empty() && intersections[0].startMD > initialMD )
|
|
{
|
|
WellPathCellIntersectionInfo firstIntersection = intersections[0];
|
|
|
|
// Add a segment from user defined MD to start of grid
|
|
cvf::Vec3d intersectionPoint = wellPathGeometry->interpolatedPointAlongWellPath( initialMD );
|
|
|
|
WellPathCellIntersectionInfo extraIntersection;
|
|
|
|
extraIntersection.globCellIndex = std::numeric_limits<size_t>::max();
|
|
extraIntersection.startPoint = intersectionPoint;
|
|
extraIntersection.endPoint = firstIntersection.startPoint;
|
|
extraIntersection.startMD = initialMD;
|
|
extraIntersection.endMD = firstIntersection.startMD;
|
|
extraIntersection.intersectedCellFaceIn = cvf::StructGridInterface::NO_FACE;
|
|
extraIntersection.intersectedCellFaceOut =
|
|
cvf::StructGridInterface::oppositeFace( firstIntersection.intersectedCellFaceIn );
|
|
extraIntersection.intersectionLengthsInCellCS = cvf::Vec3d::ZERO;
|
|
|
|
filteredIntersections.push_back( extraIntersection );
|
|
}
|
|
|
|
const double epsilon = 1.0e-3;
|
|
|
|
for ( const WellPathCellIntersectionInfo& intersection : intersections )
|
|
{
|
|
if ( ( intersection.endMD - initialMD ) < epsilon )
|
|
{
|
|
// Skip all intersections before initial measured depth
|
|
continue;
|
|
}
|
|
|
|
if ( ( intersection.startMD - initialMD ) > epsilon )
|
|
{
|
|
filteredIntersections.push_back( intersection );
|
|
}
|
|
else
|
|
{
|
|
// InitialMD is inside intersection, split based on intersection point
|
|
|
|
cvf::Vec3d intersectionPoint = wellPathGeometry->interpolatedPointAlongWellPath( initialMD );
|
|
|
|
WellPathCellIntersectionInfo extraIntersection;
|
|
|
|
extraIntersection.globCellIndex = intersection.globCellIndex;
|
|
extraIntersection.startPoint = intersectionPoint;
|
|
extraIntersection.endPoint = intersection.endPoint;
|
|
extraIntersection.startMD = initialMD;
|
|
extraIntersection.endMD = intersection.endMD;
|
|
extraIntersection.intersectedCellFaceIn = cvf::StructGridInterface::NO_FACE;
|
|
extraIntersection.intersectedCellFaceOut = intersection.intersectedCellFaceOut;
|
|
|
|
const RigMainGrid* grid = eclipseCase->mainGrid();
|
|
|
|
if ( intersection.globCellIndex < grid->cellCount() )
|
|
{
|
|
extraIntersection.intersectionLengthsInCellCS =
|
|
RigWellPathIntersectionTools::calculateLengthInCell( grid,
|
|
intersection.globCellIndex,
|
|
intersectionPoint,
|
|
intersection.endPoint );
|
|
}
|
|
else
|
|
{
|
|
extraIntersection.intersectionLengthsInCellCS = cvf::Vec3d::ZERO;
|
|
}
|
|
|
|
filteredIntersections.push_back( extraIntersection );
|
|
}
|
|
}
|
|
|
|
return filteredIntersections;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::createWellPathSegments(
|
|
gsl::not_null<RicMswBranch*> branch,
|
|
const std::vector<WellPathCellIntersectionInfo>& cellSegmentIntersections,
|
|
const std::vector<const RimPerforationInterval*>& perforationIntervals,
|
|
const RimWellPath* wellPath,
|
|
int timeStep,
|
|
const RimEclipseCase* eclipseCase,
|
|
bool* foundSubGridIntersections )
|
|
{
|
|
// Intersections along the well path with grid geometry is handled by well log extraction tools.
|
|
// The threshold in RigWellLogExtractionTools::isEqualDepth is currently set to 0.1m, and this
|
|
// is a pretty large threshold based on the indicated threshold of 0.001m for MSW segments
|
|
const double segmentLengthThreshold = 1.0e-3;
|
|
|
|
for ( const auto& cellIntInfo : cellSegmentIntersections )
|
|
{
|
|
const double segmentLength = std::fabs( cellIntInfo.endMD - cellIntInfo.startMD );
|
|
|
|
if ( segmentLength > segmentLengthThreshold )
|
|
{
|
|
auto segment = std::make_unique<RicMswSegment>( QString( "%1 segment" ).arg( branch->label() ),
|
|
cellIntInfo.startMD,
|
|
cellIntInfo.endMD,
|
|
cellIntInfo.startTVD(),
|
|
cellIntInfo.endTVD() );
|
|
|
|
for ( const RimPerforationInterval* interval : perforationIntervals )
|
|
{
|
|
double overlapStart = std::max( interval->startMD(), segment->startMD() );
|
|
double overlapEnd = std::min( interval->endMD(), segment->endMD() );
|
|
double overlap = std::max( 0.0, overlapEnd - overlapStart );
|
|
if ( overlap > 0.0 )
|
|
{
|
|
double overlapStartTVD =
|
|
-wellPath->wellPathGeometry()->interpolatedPointAlongWellPath( overlapStart ).z();
|
|
auto intervalCompletion =
|
|
std::make_unique<RicMswPerforation>( interval->name(), wellPath, overlapStart, overlapStartTVD );
|
|
std::vector<RigCompletionData> completionData =
|
|
generatePerforationIntersections( wellPath, interval, timeStep, eclipseCase );
|
|
assignPerforationIntersections( completionData,
|
|
intervalCompletion.get(),
|
|
cellIntInfo,
|
|
overlapStart,
|
|
overlapEnd,
|
|
foundSubGridIntersections );
|
|
segment->addCompletion( std::move( intervalCompletion ) );
|
|
}
|
|
}
|
|
branch->addSegment( std::move( segment ) );
|
|
}
|
|
else
|
|
{
|
|
QString text =
|
|
QString( "Skipping segment , threshold = %1, length = %2" ).arg( segmentLengthThreshold ).arg( segmentLength );
|
|
RiaLogging::info( text );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::createValveCompletions(
|
|
gsl::not_null<RicMswBranch*> branch,
|
|
const std::vector<const RimPerforationInterval*>& perforationIntervals,
|
|
const RimWellPath* wellPath,
|
|
RiaDefines::EclipseUnitSystem unitSystem )
|
|
{
|
|
int nMainSegment = 0;
|
|
auto segments = branch->segments();
|
|
for ( auto segment : segments )
|
|
{
|
|
std::unique_ptr<RicMswPerforationICV> ICV;
|
|
std::unique_ptr<RicMswPerforationICD> superICD;
|
|
std::unique_ptr<RicMswPerforationAICD> superAICD;
|
|
|
|
double totalICDOverlap = 0.0;
|
|
double totalAICDOverlap = 0.0;
|
|
|
|
for ( const RimPerforationInterval* interval : perforationIntervals )
|
|
{
|
|
if ( !interval->isChecked() ) continue;
|
|
|
|
std::vector<const RimWellPathValve*> perforationValves;
|
|
interval->descendantsIncludingThisOfType( perforationValves );
|
|
|
|
for ( const RimWellPathValve* valve : perforationValves )
|
|
{
|
|
if ( !valve->isChecked() ) continue;
|
|
|
|
for ( size_t nSubValve = 0u; nSubValve < valve->valveLocations().size(); ++nSubValve )
|
|
{
|
|
double valveMD = valve->valveLocations()[nSubValve];
|
|
|
|
std::pair<double, double> valveSegment = valve->valveSegments()[nSubValve];
|
|
double overlapStart = std::max( valveSegment.first, segment->startMD() );
|
|
double overlapEnd = std::min( valveSegment.second, segment->endMD() );
|
|
double overlap = std::max( 0.0, overlapEnd - overlapStart );
|
|
|
|
double exportStartMD = valveMD;
|
|
double exportEndMD = valveMD + 0.1;
|
|
|
|
double exportStartTVD = RicMswTableFormatterTools::tvdFromMeasuredDepth( wellPath, exportStartMD );
|
|
double exportEndTVD = RicMswTableFormatterTools::tvdFromMeasuredDepth( wellPath, exportEndMD );
|
|
|
|
double overlapStartTVD = RicMswTableFormatterTools::tvdFromMeasuredDepth( wellPath, overlapStart );
|
|
double overlapEndTVD = RicMswTableFormatterTools::tvdFromMeasuredDepth( wellPath, overlapEnd );
|
|
|
|
if ( segment->startMD() <= valveMD && valveMD < segment->endMD() )
|
|
{
|
|
if ( valve->componentType() == RiaDefines::WellPathComponentType::AICD )
|
|
{
|
|
QString valveLabel =
|
|
QString( "%1 #%2" ).arg( "Combined Valve for segment" ).arg( nMainSegment + 2 );
|
|
auto subSegment = std::make_unique<RicMswSegment>( "Valve segment",
|
|
exportStartMD,
|
|
exportEndMD,
|
|
exportStartTVD,
|
|
exportEndTVD );
|
|
|
|
superAICD = std::make_unique<RicMswPerforationAICD>( valveLabel,
|
|
wellPath,
|
|
exportStartMD,
|
|
exportStartTVD,
|
|
valve );
|
|
superAICD->addSegment( std::move( subSegment ) );
|
|
}
|
|
else if ( valve->componentType() == RiaDefines::WellPathComponentType::ICD )
|
|
{
|
|
QString valveLabel =
|
|
QString( "%1 #%2" ).arg( "Combined Valve for segment" ).arg( nMainSegment + 2 );
|
|
auto subSegment = std::make_unique<RicMswSegment>( "Valve segment",
|
|
exportStartMD,
|
|
exportEndMD,
|
|
exportStartTVD,
|
|
exportEndTVD );
|
|
|
|
superICD = std::make_unique<RicMswPerforationICD>( valveLabel,
|
|
wellPath,
|
|
exportStartMD,
|
|
exportStartTVD,
|
|
valve );
|
|
superICD->addSegment( std::move( subSegment ) );
|
|
}
|
|
else if ( valve->componentType() == RiaDefines::WellPathComponentType::ICV )
|
|
{
|
|
QString valveLabel =
|
|
QString( "ICV %1 at segment #%2" ).arg( valve->name() ).arg( nMainSegment + 2 );
|
|
auto subSegment = std::make_unique<RicMswSegment>( "Valve segment",
|
|
exportStartMD,
|
|
exportEndMD,
|
|
exportStartTVD,
|
|
exportEndTVD );
|
|
|
|
ICV = std::make_unique<RicMswPerforationICV>( valveLabel,
|
|
wellPath,
|
|
exportStartMD,
|
|
exportStartTVD,
|
|
valve );
|
|
ICV->addSegment( std::move( subSegment ) );
|
|
}
|
|
}
|
|
else if ( overlap > 0.0 &&
|
|
( valve->componentType() == RiaDefines::WellPathComponentType::ICD && !superICD ) )
|
|
{
|
|
QString valveLabel =
|
|
QString( "%1 #%2" ).arg( "Combined Valve for segment" ).arg( nMainSegment + 2 );
|
|
|
|
auto subSegment = std::make_unique<RicMswSegment>( "Valve segment",
|
|
exportStartMD,
|
|
exportEndMD,
|
|
exportStartTVD,
|
|
exportEndTVD );
|
|
superICD =
|
|
std::make_unique<RicMswPerforationICD>( valveLabel, wellPath, exportStartMD, exportStartTVD, valve );
|
|
superICD->addSegment( std::move( subSegment ) );
|
|
}
|
|
else if ( overlap > 0.0 &&
|
|
( valve->componentType() == RiaDefines::WellPathComponentType::AICD && !superAICD ) )
|
|
{
|
|
QString valveLabel =
|
|
QString( "%1 #%2" ).arg( "Combined Valve for segment" ).arg( nMainSegment + 2 );
|
|
|
|
auto subSegment = std::make_unique<RicMswSegment>( "Valve segment",
|
|
exportStartMD,
|
|
exportEndMD,
|
|
exportStartTVD,
|
|
exportEndTVD );
|
|
superAICD = std::make_unique<RicMswPerforationAICD>( valveLabel,
|
|
wellPath,
|
|
exportStartMD,
|
|
exportStartTVD,
|
|
valve );
|
|
superAICD->addSegment( std::move( subSegment ) );
|
|
}
|
|
|
|
if ( valve->componentType() == RiaDefines::WellPathComponentType::AICD )
|
|
{
|
|
totalAICDOverlap += overlap;
|
|
}
|
|
else if ( valve->componentType() == RiaDefines::WellPathComponentType::ICD )
|
|
{
|
|
totalICDOverlap += overlap;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ICV )
|
|
{
|
|
segment->addCompletion( std::move( ICV ) );
|
|
}
|
|
else
|
|
{
|
|
if ( totalICDOverlap > 0.0 || totalAICDOverlap > 0.0 )
|
|
{
|
|
if ( totalAICDOverlap > totalICDOverlap )
|
|
{
|
|
segment->addCompletion( std::move( superAICD ) );
|
|
}
|
|
else
|
|
{
|
|
segment->addCompletion( std::move( superICD ) );
|
|
}
|
|
}
|
|
}
|
|
nMainSegment++;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::assignValveContributionsToSuperICDsOrAICDs(
|
|
gsl::not_null<RicMswBranch*> branch,
|
|
const std::vector<const RimPerforationInterval*>& perforationIntervals,
|
|
const std::vector<WellPathCellIntersectionInfo>& wellPathIntersections,
|
|
const RigActiveCellInfo* activeCellInfo,
|
|
RiaDefines::EclipseUnitSystem unitSystem )
|
|
{
|
|
using ValveContributionMap = std::map<RicMswCompletion*, std::vector<const RimWellPathValve*>>;
|
|
|
|
ValveContributionMap assignedRegularValves;
|
|
|
|
std::map<RicMswSegment*, std::unique_ptr<RicMswValveAccumulator>> accumulators;
|
|
|
|
for ( auto segment : branch->segments() )
|
|
{
|
|
RicMswValve* superValve = nullptr;
|
|
for ( auto completion : segment->completions() )
|
|
{
|
|
auto valve = dynamic_cast<RicMswValve*>( completion );
|
|
if ( valve )
|
|
{
|
|
superValve = valve;
|
|
break;
|
|
}
|
|
}
|
|
if ( dynamic_cast<RicMswPerforationICD*>( superValve ) )
|
|
{
|
|
accumulators[segment] = std::make_unique<RicMswICDAccumulator>( superValve, unitSystem );
|
|
}
|
|
else if ( dynamic_cast<RicMswPerforationAICD*>( superValve ) )
|
|
{
|
|
accumulators[segment] = std::make_unique<RicMswAICDAccumulator>( superValve, unitSystem );
|
|
}
|
|
}
|
|
|
|
for ( const RimPerforationInterval* interval : perforationIntervals )
|
|
{
|
|
if ( !interval->isChecked() ) continue;
|
|
|
|
std::vector<const RimWellPathValve*> perforationValves;
|
|
interval->descendantsIncludingThisOfType( perforationValves );
|
|
|
|
double totalPerforationLength = 0.0;
|
|
for ( const RimWellPathValve* valve : perforationValves )
|
|
{
|
|
if ( !valve->isChecked() ) continue;
|
|
|
|
for ( auto segment : branch->segments() )
|
|
{
|
|
double intervalOverlapStart = std::max( interval->startMD(), segment->startMD() );
|
|
double intervalOverlapEnd = std::min( interval->endMD(), segment->endMD() );
|
|
auto intervalOverlapWithActiveCells = calculateOverlapWithActiveCells( intervalOverlapStart,
|
|
intervalOverlapEnd,
|
|
wellPathIntersections,
|
|
activeCellInfo );
|
|
|
|
totalPerforationLength += intervalOverlapWithActiveCells.second - intervalOverlapWithActiveCells.first;
|
|
}
|
|
}
|
|
|
|
for ( const RimWellPathValve* valve : perforationValves )
|
|
{
|
|
if ( !valve->isChecked() ) continue;
|
|
|
|
for ( auto segment : branch->segments() )
|
|
{
|
|
double intervalOverlapStart = std::max( interval->startMD(), segment->startMD() );
|
|
double intervalOverlapEnd = std::min( interval->endMD(), segment->endMD() );
|
|
|
|
auto intervalOverlapWithActiveCells = calculateOverlapWithActiveCells( intervalOverlapStart,
|
|
intervalOverlapEnd,
|
|
wellPathIntersections,
|
|
activeCellInfo );
|
|
|
|
double overlapLength = intervalOverlapWithActiveCells.second - intervalOverlapWithActiveCells.first;
|
|
if ( overlapLength > 0.0 )
|
|
{
|
|
auto it = accumulators.find( segment );
|
|
|
|
if ( it != accumulators.end() )
|
|
{
|
|
it->second->accumulateValveParameters( valve, overlapLength, totalPerforationLength );
|
|
assignedRegularValves[it->second->superValve()].push_back( valve );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( const auto& accumulator : accumulators )
|
|
{
|
|
accumulator.second->applyToSuperValve();
|
|
}
|
|
|
|
for ( auto regularValvePair : assignedRegularValves )
|
|
{
|
|
if ( !regularValvePair.second.empty() )
|
|
{
|
|
QStringList valveLabels;
|
|
for ( const RimWellPathValve* regularValve : regularValvePair.second )
|
|
{
|
|
QString valveLabel = QString( "%1" ).arg( regularValve->name() );
|
|
valveLabels.push_back( valveLabel );
|
|
}
|
|
QString valveContribLabel = QString( " with contribution from: %1" ).arg( valveLabels.join( ", " ) );
|
|
regularValvePair.first->setLabel( regularValvePair.first->label() + valveContribLabel );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::moveIntersectionsToICVs(
|
|
gsl::not_null<RicMswBranch*> branch,
|
|
const std::vector<const RimPerforationInterval*>& perforationIntervals,
|
|
RiaDefines::EclipseUnitSystem unitSystem )
|
|
{
|
|
std::map<const RimWellPathValve*, RicMswPerforationICV*> icvCompletionMap;
|
|
|
|
for ( auto segment : branch->segments() )
|
|
{
|
|
for ( auto completion : segment->completions() )
|
|
{
|
|
auto icv = dynamic_cast<RicMswPerforationICV*>( completion );
|
|
if ( icv )
|
|
{
|
|
icvCompletionMap[icv->wellPathValve()] = icv;
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( auto segment : branch->segments() )
|
|
{
|
|
std::vector<RicMswCompletion*> perforations;
|
|
for ( auto completion : segment->completions() )
|
|
{
|
|
if ( completion->completionType() == RigCompletionData::CompletionType::PERFORATION )
|
|
{
|
|
perforations.push_back( completion );
|
|
}
|
|
}
|
|
|
|
for ( const RimPerforationInterval* interval : perforationIntervals )
|
|
{
|
|
if ( !interval->isChecked() ) continue;
|
|
|
|
std::vector<const RimWellPathValve*> perforationValves;
|
|
interval->descendantsIncludingThisOfType( perforationValves );
|
|
|
|
for ( const RimWellPathValve* valve : perforationValves )
|
|
{
|
|
if ( !valve->isChecked() ) continue;
|
|
if ( valve->componentType() != RiaDefines::WellPathComponentType::ICV ) continue;
|
|
|
|
auto icvIt = icvCompletionMap.find( valve );
|
|
if ( icvIt == icvCompletionMap.end() ) continue;
|
|
|
|
auto icvCompletion = icvIt->second;
|
|
CVF_ASSERT( icvCompletion );
|
|
|
|
std::pair<double, double> valveSegment = valve->valveSegments().front();
|
|
double overlapStart = std::max( valveSegment.first, segment->startMD() );
|
|
double overlapEnd = std::min( valveSegment.second, segment->endMD() );
|
|
double overlap = std::max( 0.0, overlapEnd - overlapStart );
|
|
|
|
if ( overlap > 0.0 )
|
|
{
|
|
CVF_ASSERT( icvCompletion->segments().size() == 1u );
|
|
for ( auto perforationPtr : perforations )
|
|
{
|
|
for ( auto subSegmentPtr : perforationPtr->segments() )
|
|
{
|
|
for ( auto intersectionPtr : subSegmentPtr->intersections() )
|
|
{
|
|
icvCompletion->segments()[0]->addIntersection( intersectionPtr );
|
|
}
|
|
}
|
|
segment->removeCompletion( perforationPtr );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::moveIntersectionsToSuperICDsOrAICDs( gsl::not_null<RicMswBranch*> branch )
|
|
{
|
|
for ( auto segment : branch->segments() )
|
|
{
|
|
RicMswCompletion* superValve = nullptr;
|
|
std::vector<RicMswCompletion*> perforations;
|
|
for ( auto completion : segment->completions() )
|
|
{
|
|
if ( RigCompletionData::isPerforationValve( completion->completionType() ) )
|
|
{
|
|
superValve = completion;
|
|
}
|
|
else
|
|
{
|
|
CVF_ASSERT( completion->completionType() == RigCompletionData::CompletionType::PERFORATION );
|
|
perforations.push_back( completion );
|
|
}
|
|
}
|
|
|
|
if ( superValve == nullptr ) continue;
|
|
|
|
CVF_ASSERT( superValve->segments().size() == 1u );
|
|
// Remove and take over ownership of the superValve completion
|
|
auto completionPtr = segment->removeCompletion( superValve );
|
|
for ( auto perforation : perforations )
|
|
{
|
|
for ( auto subSegment : perforation->segments() )
|
|
{
|
|
for ( auto intersectionPtr : subSegment->intersections() )
|
|
{
|
|
completionPtr->segments()[0]->addIntersection( intersectionPtr );
|
|
}
|
|
}
|
|
}
|
|
// Remove all completions and re-add the super valve
|
|
segment->completions().clear();
|
|
segment->addCompletion( std::move( completionPtr ) );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::assignFishbonesLateralIntersections( const RimEclipseCase* eclipseCase,
|
|
const RimWellPath* wellPath,
|
|
const RimFishbones* fishbonesSubs,
|
|
gsl::not_null<RicMswSegment*> segment,
|
|
bool* foundSubGridIntersections,
|
|
double maxSegmentLength,
|
|
RiaDefines::EclipseUnitSystem unitSystem )
|
|
{
|
|
CVF_ASSERT( foundSubGridIntersections != nullptr );
|
|
|
|
const RigMainGrid* grid = eclipseCase->eclipseCaseData()->mainGrid();
|
|
|
|
for ( auto completion : segment->completions() )
|
|
{
|
|
if ( completion->completionType() != RigCompletionData::CompletionType::FISHBONES )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
std::vector<std::pair<cvf::Vec3d, double>> lateralCoordMDPairs =
|
|
fishbonesSubs->coordsAndMDForLateral( segment->subIndex(), completion->index() );
|
|
|
|
if ( lateralCoordMDPairs.empty() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
std::vector<cvf::Vec3d> lateralCoords;
|
|
std::vector<double> lateralMDs;
|
|
|
|
lateralCoords.reserve( lateralCoordMDPairs.size() );
|
|
lateralMDs.reserve( lateralCoordMDPairs.size() );
|
|
|
|
for ( auto& coordMD : lateralCoordMDPairs )
|
|
{
|
|
lateralCoords.push_back( coordMD.first );
|
|
lateralMDs.push_back( coordMD.second );
|
|
}
|
|
|
|
std::vector<WellPathCellIntersectionInfo> intersections =
|
|
RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath( eclipseCase->eclipseCaseData(),
|
|
wellPath->name(),
|
|
lateralCoords,
|
|
lateralMDs );
|
|
|
|
RigWellPath pathGeometry( lateralCoords, lateralMDs );
|
|
|
|
double previousExitMD = lateralMDs.front();
|
|
double previousExitTVD = -lateralCoords.front().z();
|
|
|
|
for ( const auto& cellIntInfo : intersections )
|
|
{
|
|
size_t localGridCellIndex = 0u;
|
|
const RigGridBase* localGrid =
|
|
grid->gridAndGridLocalIdxFromGlobalCellIdx( cellIntInfo.globCellIndex, &localGridCellIndex );
|
|
QString gridName;
|
|
if ( localGrid != grid )
|
|
{
|
|
gridName = QString::fromStdString( localGrid->gridName() );
|
|
*foundSubGridIntersections = true;
|
|
}
|
|
|
|
size_t i = 0u, j = 0u, k = 0u;
|
|
localGrid->ijkFromCellIndex( localGridCellIndex, &i, &j, &k );
|
|
auto subSegment = std::make_unique<RicMswSegment>( "Sub segment",
|
|
previousExitMD,
|
|
cellIntInfo.endMD,
|
|
previousExitTVD,
|
|
cellIntInfo.endTVD(),
|
|
segment->subIndex() );
|
|
|
|
subSegment->setEquivalentDiameter( fishbonesSubs->equivalentDiameter( unitSystem ) );
|
|
subSegment->setHoleDiameter( fishbonesSubs->holeDiameter( unitSystem ) );
|
|
subSegment->setOpenHoleRoughnessFactor( fishbonesSubs->openHoleRoughnessFactor( unitSystem ) );
|
|
subSegment->setSkinFactor( fishbonesSubs->skinFactor() );
|
|
subSegment->setSourcePdmObject( fishbonesSubs );
|
|
subSegment->setIntersectedGlobalCells( { cellIntInfo.globCellIndex } );
|
|
|
|
auto intersection = std::make_shared<RicMswSegmentCellIntersection>( gridName,
|
|
cellIntInfo.globCellIndex,
|
|
cvf::Vec3st( i, j, k ),
|
|
cellIntInfo.intersectionLengthsInCellCS );
|
|
subSegment->addIntersection( std::move( intersection ) );
|
|
completion->addSegment( std::move( subSegment ) );
|
|
|
|
previousExitMD = cellIntInfo.endMD;
|
|
previousExitTVD = cellIntInfo.endTVD();
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::assignFractureCompletionsToCellSegment( const RimEclipseCase* eclipseCase,
|
|
const RimWellPath* wellPath,
|
|
const RimWellPathFracture* fracture,
|
|
const std::vector<RigCompletionData>& completionData,
|
|
gsl::not_null<RicMswSegment*> segment,
|
|
bool* foundSubGridIntersections )
|
|
{
|
|
CVF_ASSERT( foundSubGridIntersections != nullptr );
|
|
|
|
double position = fracture->fractureMD();
|
|
double width = fracture->fractureTemplate()->computeFractureWidth( fracture );
|
|
|
|
auto fractureCompletion = std::make_unique<RicMswFracture>( fracture->name(), wellPath, position, position + width );
|
|
|
|
if ( fracture->fractureTemplate()->orientationType() == RimFractureTemplate::ALONG_WELL_PATH )
|
|
{
|
|
double perforationLength = fracture->fractureTemplate()->perforationLength();
|
|
position -= 0.5 * perforationLength;
|
|
width = perforationLength;
|
|
}
|
|
|
|
auto subSegment = std::make_unique<RicMswSegment>( "Fracture segment", position, position + width, 0.0, 0.0 );
|
|
for ( const RigCompletionData& compIntersection : completionData )
|
|
{
|
|
const RigCompletionDataGridCell& cell = compIntersection.completionDataGridCell();
|
|
|
|
if ( !cell.isMainGridCell() )
|
|
{
|
|
*foundSubGridIntersections = true;
|
|
}
|
|
|
|
cvf::Vec3st localIJK( cell.localCellIndexI(), cell.localCellIndexJ(), cell.localCellIndexK() );
|
|
|
|
auto intersection = std::make_shared<RicMswSegmentCellIntersection>( cell.lgrName(),
|
|
cell.globalCellIndex(),
|
|
localIJK,
|
|
cvf::Vec3d::ZERO );
|
|
subSegment->addIntersection( intersection );
|
|
}
|
|
fractureCompletion->addSegment( std::move( subSegment ) );
|
|
segment->addCompletion( std::move( fractureCompletion ) );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RigCompletionData> RicWellPathExportMswCompletionsImpl::generatePerforationIntersections(
|
|
gsl::not_null<const RimWellPath*> wellPath,
|
|
gsl::not_null<const RimPerforationInterval*> perforationInterval,
|
|
int timeStep,
|
|
gsl::not_null<const RimEclipseCase*> eclipseCase )
|
|
{
|
|
std::vector<RigCompletionData> completionData;
|
|
const RigActiveCellInfo* activeCellInfo =
|
|
eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
|
|
|
|
auto wellPathGeometry = wellPath->wellPathGeometry();
|
|
CVF_ASSERT( wellPathGeometry );
|
|
bool hasDate = (size_t)timeStep < eclipseCase->timeStepDates().size();
|
|
bool isActive = !hasDate || perforationInterval->isActiveOnDate( eclipseCase->timeStepDates()[timeStep] );
|
|
|
|
if ( wellPath->perforationIntervalCollection()->isChecked() && perforationInterval->isChecked() && isActive )
|
|
{
|
|
std::pair<std::vector<cvf::Vec3d>, std::vector<double>> perforationPointsAndMD =
|
|
wellPathGeometry->clippedPointSubset( perforationInterval->startMD(), perforationInterval->endMD() );
|
|
|
|
std::vector<WellPathCellIntersectionInfo> intersectedCells =
|
|
RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath( eclipseCase->eclipseCaseData(),
|
|
wellPath->name(),
|
|
perforationPointsAndMD.first,
|
|
perforationPointsAndMD.second );
|
|
|
|
for ( auto& cell : intersectedCells )
|
|
{
|
|
bool cellIsActive = activeCellInfo->isActive( cell.globCellIndex );
|
|
if ( !cellIsActive ) continue;
|
|
|
|
RigCompletionData completion( wellPath->completionSettings()->wellNameForExport(),
|
|
RigCompletionDataGridCell( cell.globCellIndex, eclipseCase->mainGrid() ),
|
|
cell.startMD );
|
|
|
|
completion.setSourcePdmObject( perforationInterval );
|
|
completionData.push_back( completion );
|
|
}
|
|
}
|
|
|
|
return completionData;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::assignPerforationIntersections(
|
|
const std::vector<RigCompletionData>& completionData,
|
|
gsl::not_null<RicMswCompletion*> perforationCompletion,
|
|
const WellPathCellIntersectionInfo& cellIntInfo,
|
|
double overlapStart,
|
|
double overlapEnd,
|
|
bool* foundSubGridIntersections )
|
|
{
|
|
size_t currCellId = cellIntInfo.globCellIndex;
|
|
|
|
auto subSegment = std::make_unique<RicMswSegment>( "Perforation segment",
|
|
overlapStart,
|
|
overlapEnd,
|
|
cellIntInfo.startTVD(),
|
|
cellIntInfo.endTVD() );
|
|
for ( const RigCompletionData& compIntersection : completionData )
|
|
{
|
|
const RigCompletionDataGridCell& cell = compIntersection.completionDataGridCell();
|
|
if ( !cell.isMainGridCell() )
|
|
{
|
|
*foundSubGridIntersections = true;
|
|
}
|
|
|
|
if ( cell.globalCellIndex() != currCellId ) continue;
|
|
|
|
cvf::Vec3st localIJK( cell.localCellIndexI(), cell.localCellIndexJ(), cell.localCellIndexK() );
|
|
|
|
auto intersection = std::make_shared<RicMswSegmentCellIntersection>( cell.lgrName(),
|
|
cell.globalCellIndex(),
|
|
localIJK,
|
|
cellIntInfo.intersectionLengthsInCellCS );
|
|
subSegment->addIntersection( intersection );
|
|
}
|
|
perforationCompletion->addSegment( std::move( subSegment ) );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::assignBranchNumbersToPerforations( const RimEclipseCase* eclipseCase,
|
|
gsl::not_null<RicMswSegment*> segment,
|
|
gsl::not_null<int*> branchNumber )
|
|
{
|
|
for ( auto completion : segment->completions() )
|
|
{
|
|
if ( completion->completionType() == RigCompletionData::CompletionType::PERFORATION )
|
|
{
|
|
completion->setBranchNumber( *branchNumber );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::assignBranchNumbersToOtherCompletions( const RimEclipseCase* eclipseCase,
|
|
gsl::not_null<RicMswSegment*> segment,
|
|
gsl::not_null<int*> branchNumber )
|
|
{
|
|
for ( auto completion : segment->completions() )
|
|
{
|
|
if ( completion->completionType() != RigCompletionData::CompletionType::PERFORATION )
|
|
{
|
|
completion->setBranchNumber( ++( *branchNumber ) );
|
|
|
|
for ( auto seg : completion->segments() )
|
|
{
|
|
assignBranchNumbersToOtherCompletions( eclipseCase, seg, branchNumber );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportMswCompletionsImpl::assignBranchNumbersToBranch( const RimEclipseCase* eclipseCase,
|
|
RicMswExportInfo* exportInfo,
|
|
gsl::not_null<RicMswBranch*> branch,
|
|
gsl::not_null<int*> branchNumber )
|
|
{
|
|
branch->setBranchNumber( *branchNumber );
|
|
|
|
// Assign perforations first to ensure the same branch number as the segment
|
|
for ( auto segment : branch->segments() )
|
|
{
|
|
assignBranchNumbersToPerforations( eclipseCase, segment, branchNumber );
|
|
}
|
|
|
|
// Assign other completions with an incremented branch number
|
|
for ( auto segment : branch->segments() )
|
|
{
|
|
assignBranchNumbersToOtherCompletions( eclipseCase, segment, branchNumber );
|
|
}
|
|
|
|
( *branchNumber )++;
|
|
|
|
for ( auto childBranch : branch->branches() )
|
|
{
|
|
assignBranchNumbersToBranch( eclipseCase, exportInfo, childBranch, branchNumber );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::unique_ptr<RicMswBranch>
|
|
RicWellPathExportMswCompletionsImpl::createChildMswBranch( const RimModeledWellPath* childWellPath )
|
|
{
|
|
auto initialChildMD = childWellPath->wellPathTieIn()->tieInMeasuredDepth();
|
|
auto initialChildTVD = -childWellPath->wellPathGeometry()->interpolatedPointAlongWellPath( initialChildMD ).z();
|
|
|
|
const RimWellPathValve* outletValve = childWellPath->wellPathTieIn()->outletValve();
|
|
if ( outletValve )
|
|
{
|
|
auto branchStartingWithValve = RicMswValve::createTieInValve( QString( "%1 valve for %2" )
|
|
.arg( outletValve->componentLabel() )
|
|
.arg( childWellPath->name() ),
|
|
childWellPath,
|
|
initialChildMD,
|
|
initialChildTVD,
|
|
outletValve );
|
|
if ( branchStartingWithValve )
|
|
{
|
|
auto dummySegment =
|
|
std::make_unique<RicMswSegment>( QString( "%1 segment" ).arg( outletValve->componentLabel() ),
|
|
initialChildMD,
|
|
initialChildMD + 0.1,
|
|
initialChildTVD,
|
|
RicMswTableFormatterTools::tvdFromMeasuredDepth( childWellPath,
|
|
initialChildMD + 0.1 ) );
|
|
branchStartingWithValve->addSegment( std::move( dummySegment ) );
|
|
|
|
return branchStartingWithValve;
|
|
}
|
|
}
|
|
|
|
auto childBranch =
|
|
std::make_unique<RicMswBranch>( childWellPath->name(), childWellPath, initialChildMD, initialChildTVD );
|
|
|
|
return childBranch;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimModeledWellPath*> RicWellPathExportMswCompletionsImpl::wellPathsWithTieIn( const RimWellPath* wellPath )
|
|
{
|
|
std::vector<RimModeledWellPath*> connectedWellPaths;
|
|
{
|
|
auto wellPaths = RimProject::current()->allWellPaths();
|
|
for ( auto w : wellPaths )
|
|
{
|
|
auto modelWellPath = dynamic_cast<RimModeledWellPath*>( w );
|
|
if ( modelWellPath && modelWellPath->isEnabled() && modelWellPath->wellPathTieIn() &&
|
|
modelWellPath->wellPathTieIn()->parentWell() == wellPath )
|
|
{
|
|
connectedWellPaths.push_back( modelWellPath );
|
|
}
|
|
}
|
|
}
|
|
|
|
return connectedWellPaths;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::pair<double, double> RicWellPathExportMswCompletionsImpl::calculateOverlapWithActiveCells(
|
|
double startMD,
|
|
double endMD,
|
|
const std::vector<WellPathCellIntersectionInfo>& wellPathIntersections,
|
|
const RigActiveCellInfo* activeCellInfo )
|
|
{
|
|
for ( const WellPathCellIntersectionInfo& intersection : wellPathIntersections )
|
|
{
|
|
if ( intersection.globCellIndex < activeCellInfo->reservoirCellCount() &&
|
|
activeCellInfo->isActive( intersection.globCellIndex ) )
|
|
{
|
|
double overlapStart = std::max( startMD, intersection.startMD );
|
|
double overlapEnd = std::min( endMD, intersection.endMD );
|
|
if ( overlapEnd > overlapStart )
|
|
{
|
|
return std::make_pair( overlapStart, overlapEnd );
|
|
}
|
|
}
|
|
}
|
|
return std::make_pair( 0.0, 0.0 );
|
|
}
|