ResInsight/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathExportMswCompletionsImpl.cpp
Magne Sjaastad 2e643edc35 MSW : Restore MSW settings per completion type
Remove the common MSW settings on well path
2021-03-11 10:50:25 +01:00

2465 lines
118 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 "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 "RimPerforationCollection.h"
#include "RimPerforationInterval.h"
#include "RimWellPath.h"
#include "RimWellPathCompletions.h"
#include "RimWellPathFracture.h"
#include "RimWellPathFractureCollection.h"
#include "RimWellPathGroup.h"
#include "RimWellPathValve.h"
#include <QFile>
#include <algorithm>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForAllCompletions(
const RicExportCompletionDataSettingsUi& exportSettings,
const std::vector<RimWellPath*>& wellPaths )
{
std::shared_ptr<QFile> unifiedExportFile;
if ( exportSettings.fileSplit() == RicExportCompletionDataSettingsUi::UNIFIED_FILE )
{
QString unifiedFileName =
QString( "UnifiedCompletions_MSW_%1" ).arg( exportSettings.caseToApply->caseUserDescription() );
unifiedExportFile =
RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, unifiedFileName );
}
for ( const auto& wellPath : wellPaths )
{
std::shared_ptr<QFile> unifiedWellPathFile;
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 exportPerforations = exportSettings.includePerforations() &&
std::any_of( allCompletions.begin(), allCompletions.end(), []( auto completion ) {
return completion->isEnabled() &&
completion->componentType() ==
RiaDefines::WellPathComponentType::PERFORATION_INTERVAL;
} );
bool exportFishbones = exportSettings.includeFishbones() &&
std::any_of( allCompletions.begin(), allCompletions.end(), []( auto completion ) {
return completion->isEnabled() &&
completion->componentType() == RiaDefines::WellPathComponentType::FISHBONES;
} );
bool exportAnyCompletion = exportFractures || exportPerforations || exportFishbones;
if ( exportAnyCompletion && exportSettings.fileSplit() == RicExportCompletionDataSettingsUi::SPLIT_ON_WELL &&
!unifiedWellPathFile )
{
QString wellFileName = QString( "%1_UnifiedCompletions_MSW_%2" )
.arg( wellPath->name(), exportSettings.caseToApply->caseUserDescription() );
unifiedWellPathFile =
RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, wellFileName );
}
if ( exportFractures )
{
std::shared_ptr<QFile> fractureExportFile;
if ( unifiedExportFile )
fractureExportFile = unifiedExportFile;
else if ( unifiedWellPathFile )
fractureExportFile = unifiedWellPathFile;
else
{
QString fileName =
QString( "%1_Fracture_MSW_%2" ).arg( wellPath->name(), exportSettings.caseToApply->caseUserDescription() );
fractureExportFile =
RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, fileName );
}
exportWellSegmentsForFractures( exportSettings.caseToApply,
fractureExportFile,
wellPath,
exportSettings.exportDataSourceAsComment() );
}
if ( exportPerforations )
{
std::shared_ptr<QFile> perforationsExportFile;
if ( unifiedExportFile )
perforationsExportFile = unifiedExportFile;
else if ( unifiedWellPathFile )
perforationsExportFile = unifiedWellPathFile;
else
{
QString fileName = QString( "%1_Perforation_MSW_%2" )
.arg( wellPath->name(), exportSettings.caseToApply->caseUserDescription() );
perforationsExportFile =
RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, fileName );
}
exportWellSegmentsForPerforations( exportSettings.caseToApply,
perforationsExportFile,
wellPath,
exportSettings.timeStep,
exportSettings.exportDataSourceAsComment() );
}
if ( exportFishbones )
{
std::shared_ptr<QFile> fishbonesExportFile;
if ( unifiedExportFile )
fishbonesExportFile = unifiedExportFile;
else if ( unifiedWellPathFile )
fishbonesExportFile = unifiedWellPathFile;
else
{
QString fileName =
QString( "%1_Fishbones_MSW_%2" ).arg( wellPath->name(), exportSettings.caseToApply->caseUserDescription() );
fishbonesExportFile =
RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder, fileName );
}
exportWellSegmentsForFishbones( exportSettings.caseToApply,
fishbonesExportFile,
wellPath,
exportSettings.exportDataSourceAsComment() );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForFractures( RimEclipseCase* eclipseCase,
std::shared_ptr<QFile> exportFile,
const RimWellPath* wellPath,
bool exportDataSourceAsComment )
{
auto fractures = wellPath->fractureCollection()->activeFractures();
if ( eclipseCase == nullptr )
{
RiaLogging::error(
"Export Fracture Well Segments: Cannot export completions data without specified eclipse case" );
return;
}
RicMswExportInfo exportInfo = generateFracturesMswExportInfo( eclipseCase, wellPath, fractures );
QTextStream stream( exportFile.get() );
RifTextDataTableFormatter formatter( stream );
formatter.setOptionalComment( exportDataSourceAsComment );
double maxSegmentLength = wellPath->fractureCollection()->mswParameters()->maxSegmentLength();
generateWelsegsTable( formatter, exportInfo, maxSegmentLength );
generateCompsegTables( formatter, exportInfo );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForFishbones( RimEclipseCase* eclipseCase,
std::shared_ptr<QFile> exportFile,
const RimWellPath* wellPath,
bool exportDataSourceAsComment )
{
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->fishbonesCollection()->mswParameters();
auto cellIntersections = generateCellSegments( eclipseCase, wellPath, mswParameters, &initialMD );
RiaDefines::EclipseUnitSystem unitSystem = eclipseCase->eclipseCaseData()->unitsType();
RicMswExportInfo exportInfo( wellPath,
unitSystem,
wellPath->fishbonesCollection()->startMD(),
mswParameters->lengthAndDepth().text(),
mswParameters->pressureDrop().text() );
exportInfo.setLinerDiameter( mswParameters->linerDiameter( unitSystem ) );
exportInfo.setRoughnessFactor( mswParameters->roughnessFactor( unitSystem ) );
generateFishbonesMswExportInfo( eclipseCase,
wellPath,
initialMD,
cellIntersections,
fishbonesSubs,
true,
&exportInfo,
exportInfo.mainBoreBranch() );
int branchNumber = 1;
assignBranchNumbersToBranch( eclipseCase, &exportInfo, exportInfo.mainBoreBranch(), &branchNumber );
QTextStream stream( exportFile.get() );
RifTextDataTableFormatter formatter( stream );
formatter.setOptionalComment( exportDataSourceAsComment );
double maxSegmentLength = wellPath->fishbonesCollection()->mswParameters()->maxSegmentLength();
generateWelsegsTable( formatter, exportInfo, maxSegmentLength );
generateCompsegTables( formatter, exportInfo );
generateWsegvalvTable( formatter, exportInfo );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForPerforations( RimEclipseCase* eclipseCase,
std::shared_ptr<QFile> exportFile,
const RimWellPath* wellPath,
int timeStep,
bool exportDataSourceAsComment )
{
RiaDefines::EclipseUnitSystem unitSystem = eclipseCase->eclipseCaseData()->unitsType();
auto mswParameters = wellPath->perforationIntervalCollection()->mswParameters();
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() );
exportInfo.setLinerDiameter( mswParameters->linerDiameter( unitSystem ) );
exportInfo.setRoughnessFactor( mswParameters->roughnessFactor( unitSystem ) );
if ( generatePerforationsMswExportInfo( eclipseCase,
wellPath,
timeStep,
initialMD,
cellIntersections,
&exportInfo,
exportInfo.mainBoreBranch() ) )
{
int branchNumber = 1;
assignBranchNumbersToBranch( eclipseCase, &exportInfo, exportInfo.mainBoreBranch(), &branchNumber );
QTextStream stream( exportFile.get() );
RifTextDataTableFormatter formatter( stream );
double maxSegmentLength = mswParameters->maxSegmentLength();
generateWelsegsTable( formatter, exportInfo, maxSegmentLength );
generateCompsegTables( formatter, exportInfo );
generateWsegvalvTable( formatter, exportInfo );
generateWsegAicdTable( formatter, exportInfo );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateWelsegsTable( RifTextDataTableFormatter& formatter,
RicMswExportInfo& exportInfo,
double maxSegmentLength )
{
formatter.keyword( "WELSEGS" );
double startMD = exportInfo.mainBoreBranch()->startMD();
double startTVD = exportInfo.mainBoreBranch()->startTVD();
{
std::vector<RifTextDataTableColumn> header = {
RifTextDataTableColumn( "Name" ),
RifTextDataTableColumn( "Dep 1" ),
RifTextDataTableColumn( "Tlen 1" ),
RifTextDataTableColumn( "Vol 1" ),
RifTextDataTableColumn( "Len&Dep" ),
RifTextDataTableColumn( "PresDrop" ),
};
formatter.header( header );
formatter.add( exportInfo.mainBoreBranch()->wellPath()->completionSettings()->wellNameForExport() );
formatter.add( startTVD );
formatter.add( startMD );
formatter.addValueOrDefaultMarker( exportInfo.topWellBoreVolume(), RicMswExportInfo::defaultDoubleValue() );
formatter.add( exportInfo.lengthAndDepthText() );
formatter.add( QString( "'%1'" ).arg( exportInfo.pressureDropText() ) );
formatter.rowCompleted();
}
{
std::vector<RifTextDataTableColumn> header =
{ RifTextDataTableColumn( "First Seg" ),
RifTextDataTableColumn( "Last Seg" ),
RifTextDataTableColumn( "Branch Num" ),
RifTextDataTableColumn( "Outlet Seg" ),
RifTextDataTableColumn( "Length" ),
RifTextDataTableColumn( "Depth Change" ),
RifTextDataTableColumn( "Diam" ),
RifTextDataTableColumn( "Rough", RifTextDataTableDoubleFormatting( RIF_FLOAT, 7 ) ) };
formatter.header( header );
}
int segmentNumber = 2; // There's an implicit segment number 1.
writeWelsegsSegmentsRecursively( formatter, exportInfo, exportInfo.mainBoreBranch(), &segmentNumber, maxSegmentLength );
formatter.tableCompleted();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::writeWelsegsSegmentsRecursively( RifTextDataTableFormatter& formatter,
RicMswExportInfo& exportInfo,
gsl::not_null<RicMswBranch*> branch,
gsl::not_null<int*> segmentNumber,
double maxSegmentLength,
RicMswSegment* connectedToSegment )
{
auto outletSegment = connectedToSegment;
RicMswValve* outletValve = nullptr;
auto branchSegments = branch->segments();
auto it = branchSegments.begin();
if ( outletValve = dynamic_cast<RicMswValve*>( branch.get() ); outletValve != nullptr )
{
writeValveWelsegsSegment( outletSegment, outletValve, formatter, exportInfo, maxSegmentLength, segmentNumber );
auto valveSegments = outletValve->segments();
outletSegment = valveSegments.front();
*segmentNumber = outletSegment->segmentNumber() + 1;
++it; // skip segment below
}
formatter.addOptionalComment( QString( "Segments on branch %1" ).arg( branch->label() ) );
for ( ; it != branchSegments.end(); ++it )
{
auto segment = *it;
segment->setSegmentNumber( *segmentNumber );
if ( segment->subIndex() != cvf::UNDEFINED_SIZE_T )
{
QString comment = segment->label() + QString( ", sub %1" ).arg( segment->subIndex() );
formatter.addOptionalComment( comment );
}
writeWelsegsSegment( segment, outletSegment, formatter, exportInfo, maxSegmentLength, branch, segmentNumber );
outletSegment = segment;
for ( auto& completion : segment->completions() )
{
// For a well with perforation intervals, the WELSEGS segments are reported twice if if we include the
// RicMswPerforation completions. Investigate when this class is intended to be exported to file
auto performationMsw = dynamic_cast<RicMswPerforation*>( completion );
if ( performationMsw ) continue;
auto segmentValve = dynamic_cast<RicMswValve*>( completion );
if ( segmentValve != nullptr )
{
writeValveWelsegsSegment( segment, segmentValve, formatter, exportInfo, maxSegmentLength, segmentNumber );
outletValve = segmentValve;
outletSegment = segment;
}
else
{
// If we have a valve, the outlet segment is the valve's segment
RicMswSegment* outletSegment =
outletValve && outletValve->segmentCount() > 0 ? outletValve->segments().front() : segment;
writeCompletionWelsegsSegments( outletSegment, completion, formatter, exportInfo, maxSegmentLength, segmentNumber );
}
}
}
for ( auto childBranch : branch->branches() )
{
writeWelsegsSegmentsRecursively( formatter, exportInfo, childBranch, segmentNumber, maxSegmentLength, outletSegment );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::writeWelsegsCompletionCommentHeader( RifTextDataTableFormatter& formatter,
RigCompletionData::CompletionType completionType )
{
if ( completionType == RigCompletionData::CT_UNDEFINED )
{
formatter.addOptionalComment( "Main stem" );
}
else if ( completionType == RigCompletionData::FISHBONES_ICD )
{
formatter.addOptionalComment( "Fishbone Laterals" );
formatter.addOptionalComment( "Diam: MSW - Tubing Radius" );
formatter.addOptionalComment( "Rough: MSW - Open Hole Roughness Factor" );
}
else if ( RigCompletionData::isPerforationValve( completionType ) )
{
formatter.addOptionalComment( "Perforation Valve Segments" );
formatter.addOptionalComment( "Diam: MSW - Tubing Radius" );
formatter.addOptionalComment( "Rough: MSW - Open Hole Roughness Factor" );
}
else if ( completionType == RigCompletionData::FRACTURE )
{
formatter.addOptionalComment( "Fracture Segments" );
formatter.addOptionalComment( "Diam: MSW - Default Dummy" );
formatter.addOptionalComment( "Rough: MSW - Default Dummy" );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateCompsegTables( RifTextDataTableFormatter& formatter,
RicMswExportInfo& exportInfo )
{
/*
* TODO: Creating the regular perforation COMPSEGS table should come in here, before the others
* should take precedence by appearing later in the output. See #3230.
*/
bool headerGenerated = false;
std::set<cvf::Vec3st, CvfVec3stComparator> intersectedCells;
{
std::set<RigCompletionData::CompletionType> perforationTypes = { RigCompletionData::PERFORATION,
RigCompletionData::PERFORATION_ICD,
RigCompletionData::PERFORATION_ICV,
RigCompletionData::PERFORATION_AICD };
generateCompsegTable( formatter,
exportInfo,
exportInfo.mainBoreBranch(),
false,
perforationTypes,
&headerGenerated,
&intersectedCells );
if ( exportInfo.hasSubGridIntersections() )
{
generateCompsegTable( formatter,
exportInfo,
exportInfo.mainBoreBranch(),
true,
perforationTypes,
&headerGenerated,
&intersectedCells );
}
}
{
std::set<RigCompletionData::CompletionType> fishbonesTypes = { RigCompletionData::FISHBONES_ICD,
RigCompletionData::FISHBONES };
generateCompsegTable( formatter,
exportInfo,
exportInfo.mainBoreBranch(),
false,
fishbonesTypes,
&headerGenerated,
&intersectedCells );
if ( exportInfo.hasSubGridIntersections() )
{
generateCompsegTable( formatter,
exportInfo,
exportInfo.mainBoreBranch(),
true,
fishbonesTypes,
&headerGenerated,
&intersectedCells );
}
}
{
std::set<RigCompletionData::CompletionType> fractureTypes = { RigCompletionData::FRACTURE };
generateCompsegTable( formatter,
exportInfo,
exportInfo.mainBoreBranch(),
false,
fractureTypes,
&headerGenerated,
&intersectedCells );
if ( exportInfo.hasSubGridIntersections() )
{
generateCompsegTable( formatter,
exportInfo,
exportInfo.mainBoreBranch(),
true,
fractureTypes,
&headerGenerated,
&intersectedCells );
}
}
if ( headerGenerated )
{
formatter.tableCompleted();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateCompsegTable(
RifTextDataTableFormatter& formatter,
RicMswExportInfo& exportInfo,
gsl::not_null<const RicMswBranch*> branch,
bool exportSubGridIntersections,
const std::set<RigCompletionData::CompletionType>& exportCompletionTypes,
gsl::not_null<bool*> headerGenerated,
gsl::not_null<std::set<cvf::Vec3st, CvfVec3stComparator>*> intersectedCells )
{
for ( auto segment : branch->segments() )
{
for ( auto completion : segment->completions() )
{
if ( completion->segments().empty() || !exportCompletionTypes.count( completion->completionType() ) )
continue;
if ( !*headerGenerated )
{
generateCompsegHeader( formatter, exportInfo, completion->completionType(), exportSubGridIntersections );
*headerGenerated = true;
}
bool isPerforationValve = completion->completionType() == RigCompletionData::PERFORATION_ICD ||
completion->completionType() == RigCompletionData::PERFORATION_AICD ||
completion->completionType() == RigCompletionData::PERFORATION_ICV;
for ( auto subSegment : completion->segments() )
{
for ( auto intersection : subSegment->intersections() )
{
bool isSubGridIntersection = !intersection->gridName().isEmpty();
if ( isSubGridIntersection != exportSubGridIntersections ) continue;
double startLength = subSegment->startMD();
double endLength = subSegment->endMD();
if ( isPerforationValve )
{
startLength = segment->startMD();
endLength = segment->endMD();
}
cvf::Vec3st ijk = intersection->gridLocalCellIJK();
if ( !intersectedCells->count( ijk ) )
{
if ( exportSubGridIntersections )
{
formatter.add( intersection->gridName() );
}
formatter.addOneBasedCellIndex( ijk.x() ).addOneBasedCellIndex( ijk.y() ).addOneBasedCellIndex(
ijk.z() );
formatter.add( completion->branchNumber() );
formatter.add( startLength );
formatter.add( endLength );
formatter.rowCompleted();
intersectedCells->insert( ijk );
}
}
}
}
}
for ( auto childBranch : branch->branches() )
{
generateCompsegTable( formatter,
exportInfo,
childBranch,
exportSubGridIntersections,
exportCompletionTypes,
headerGenerated,
intersectedCells );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateCompsegHeader( RifTextDataTableFormatter& formatter,
RicMswExportInfo& exportInfo,
RigCompletionData::CompletionType completionType,
bool exportSubGridIntersections )
{
if ( exportSubGridIntersections )
{
formatter.keyword( "COMPSEGL" );
}
else
{
formatter.keyword( "COMPSEGS" );
}
if ( completionType == RigCompletionData::FISHBONES_ICD )
{
formatter.comment( "Fishbones" );
}
else if ( completionType == RigCompletionData::FRACTURE )
{
formatter.comment( "Fractures" );
}
{
std::vector<RifTextDataTableColumn> header = { RifTextDataTableColumn( "Name" ) };
formatter.header( header );
formatter.add( exportInfo.mainBoreBranch()->wellPath()->completionSettings()->wellNameForExport() );
formatter.rowCompleted();
}
{
std::vector<RifTextDataTableColumn> allHeaders;
if ( exportSubGridIntersections )
{
allHeaders.push_back( RifTextDataTableColumn( "Grid" ) );
}
std::vector<RifTextDataTableColumn> commonHeaders = { RifTextDataTableColumn( "I" ),
RifTextDataTableColumn( "J" ),
RifTextDataTableColumn( "K" ),
RifTextDataTableColumn( "Branch no" ),
RifTextDataTableColumn( "Start Length" ),
RifTextDataTableColumn( "End Length" ),
RifTextDataTableColumn( "Dir Pen" ),
RifTextDataTableColumn( "End Range" ),
RifTextDataTableColumn( "Connection Depth" ) };
allHeaders.insert( allHeaders.end(), commonHeaders.begin(), commonHeaders.end() );
formatter.header( allHeaders );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateWsegvalvTable( RifTextDataTableFormatter& formatter,
RicMswExportInfo& exportInfo )
{
bool foundValve = false;
for ( auto segment : exportInfo.mainBoreBranch()->segments() )
{
for ( auto completion : segment->completions() )
{
if ( RigCompletionData::isWsegValveTypes( completion->completionType() ) )
{
if ( !foundValve )
{
formatter.keyword( "WSEGVALV" );
std::vector<RifTextDataTableColumn> header = {
RifTextDataTableColumn( "Well Name" ),
RifTextDataTableColumn( "Seg No" ),
RifTextDataTableColumn( "Cv" ),
RifTextDataTableColumn( "Ac" ),
};
formatter.header( header );
foundValve = true;
}
auto wsegValve = static_cast<RicMswWsegValve*>( completion );
if ( !wsegValve->segments().empty() )
{
CVF_ASSERT( wsegValve->segments().size() == 1u );
auto firstSubSegment = wsegValve->segments().front();
if ( !firstSubSegment->intersections().empty() )
{
if ( wsegValve->completionType() == RigCompletionData::PERFORATION_ICD ||
wsegValve->completionType() == RigCompletionData::PERFORATION_ICV )
{
formatter.addOptionalComment( wsegValve->label() );
}
formatter.add( exportInfo.mainBoreBranch()->wellPath()->completionSettings()->wellNameForExport() );
formatter.add( firstSubSegment->segmentNumber() );
formatter.add( wsegValve->flowCoefficient() );
formatter.add( QString( "%1" ).arg( wsegValve->area(), 8, 'g', 4 ) );
formatter.rowCompleted();
}
}
}
}
}
if ( foundValve )
{
formatter.tableCompleted();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateWsegAicdTable( RifTextDataTableFormatter& formatter,
RicMswExportInfo& exportInfo )
{
RifTextDataTableFormatter tighterFormatter( formatter );
tighterFormatter.setColumnSpacing( 1 );
tighterFormatter.setTableRowPrependText( " " );
bool foundValve = false;
for ( auto segment : exportInfo.mainBoreBranch()->segments() )
{
for ( auto completion : segment->completions() )
{
if ( completion->completionType() == RigCompletionData::PERFORATION_AICD )
{
auto aicd = static_cast<RicMswPerforationAICD*>( completion );
if ( aicd->isValid() )
{
if ( !foundValve )
{
std::vector<QString> columnDescriptions =
{ "Well Name",
"Segment Number",
"Segment Number",
"Strength of AICD",
"Flow Scaling Factor for AICD",
"Density of Calibration Fluid",
"Viscosity of Calibration Fluid",
"Critical water in liquid fraction for emulsions viscosity model",
"Emulsion viscosity transition region",
"Max ratio of emulsion viscosity to continuous phase viscosity",
"Flow scaling factor method",
"Maximum flow rate for AICD device",
"Volume flow rate exponent, x",
"Viscosity function exponent, y",
"Device OPEN/SHUT",
"Exponent of the oil flowing fraction in the density mixture calculation",
"Exponent of the water flowing fraction in the density mixture calculation",
"Exponent of the gas flowing fraction in the density mixture calculation",
"Exponent of the oil flowing fraction in the density viscosity calculation",
"Exponent of the water flowing fraction in the density viscosity calculation",
"Exponent of the gas flowing fraction in the density viscosity calculation" };
tighterFormatter.keyword( "WSEGAICD" );
tighterFormatter.comment( "Column Overview:" );
for ( size_t i = 0; i < columnDescriptions.size(); ++i )
{
tighterFormatter.comment(
QString( "%1: %2" ).arg( i + 1, 2, 10, QChar( '0' ) ).arg( columnDescriptions[i] ) );
}
std::vector<RifTextDataTableColumn> header;
for ( size_t i = 1; i <= 21; ++i )
{
QString cName = QString( "%1" ).arg( i, 2, 10, QChar( '0' ) );
RifTextDataTableColumn col( cName,
RifTextDataTableDoubleFormatting(
RifTextDataTableDoubleFormat::RIF_CONSISE ),
RIGHT );
header.push_back( col );
}
tighterFormatter.header( header );
foundValve = true;
}
if ( !aicd->segments().empty() )
{
CVF_ASSERT( aicd->segments().size() == 1u );
tighterFormatter.comment( aicd->label() );
tighterFormatter.add(
exportInfo.mainBoreBranch()->wellPath()->completionSettings()->wellNameForExport() ); // #1
tighterFormatter.add( aicd->segments().front()->segmentNumber() );
tighterFormatter.add( aicd->segments().front()->segmentNumber() );
std::array<double, AICD_NUM_PARAMS> values = aicd->values();
tighterFormatter.add( values[AICD_STRENGTH] );
tighterFormatter.add( aicd->flowScalingFactor() ); // #5 Flow scaling factor used when item
// #11 is set to '1'
tighterFormatter.add( values[AICD_DENSITY_CALIB_FLUID] );
tighterFormatter.add( values[AICD_VISCOSITY_CALIB_FLUID] );
tighterFormatter.addValueOrDefaultMarker( values[AICD_CRITICAL_WATER_IN_LIQUID_FRAC],
RicMswExportInfo::defaultDoubleValue() );
tighterFormatter.addValueOrDefaultMarker( values[AICD_EMULSION_VISC_TRANS_REGION],
RicMswExportInfo::defaultDoubleValue() );
tighterFormatter.addValueOrDefaultMarker( values[AICD_MAX_RATIO_EMULSION_VISC],
RicMswExportInfo::defaultDoubleValue() ); // #10
tighterFormatter.add( 1 ); // #11 : Always use method "b. Scale factor". The value of the
// scale factor is given in item #5
tighterFormatter.addValueOrDefaultMarker( values[AICD_MAX_FLOW_RATE],
RicMswExportInfo::defaultDoubleValue() );
tighterFormatter.add( values[AICD_VOL_FLOW_EXP] );
tighterFormatter.add( values[AICD_VISOSITY_FUNC_EXP] );
tighterFormatter.add( aicd->isOpen() ? "OPEN" : "SHUT" ); // #15
tighterFormatter.addValueOrDefaultMarker( values[AICD_EXP_OIL_FRAC_DENSITY],
RicMswExportInfo::defaultDoubleValue() );
tighterFormatter.addValueOrDefaultMarker( values[AICD_EXP_WATER_FRAC_DENSITY],
RicMswExportInfo::defaultDoubleValue() );
tighterFormatter.addValueOrDefaultMarker( values[AICD_EXP_GAS_FRAC_DENSITY],
RicMswExportInfo::defaultDoubleValue() );
tighterFormatter.addValueOrDefaultMarker( values[AICD_EXP_OIL_FRAC_VISCOSITY],
RicMswExportInfo::defaultDoubleValue() );
tighterFormatter.addValueOrDefaultMarker( values[AICD_EXP_WATER_FRAC_VISCOSITY],
RicMswExportInfo::defaultDoubleValue() ); // #20
tighterFormatter.addValueOrDefaultMarker( values[AICD_EXP_GAS_FRAC_VISCOSITY],
RicMswExportInfo::defaultDoubleValue() );
tighterFormatter.rowCompleted();
}
}
else
{
RiaLogging::error( QString( "Export AICD Valve (%1): Valve is invalid. At least one required "
"template parameter is not set." )
.arg( aicd->label() ) );
}
}
}
}
if ( foundValve )
{
tighterFormatter.tableCompleted();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<std::pair<double, double>>
RicWellPathExportMswCompletionsImpl::createSubSegmentMDPairs( double startMD, double endMD, double maxSegmentLength )
{
int subSegmentCount = (int)( std::trunc( ( endMD - startMD ) / maxSegmentLength ) + 1 );
double subSegmentLength = ( endMD - startMD ) / subSegmentCount;
std::vector<std::pair<double, double>> subSegmentMDPairs;
double subStartMD = startMD;
double subEndMD = startMD + subSegmentLength;
for ( int i = 0; i < subSegmentCount; ++i )
{
subSegmentMDPairs.push_back( std::make_pair( subStartMD, subEndMD ) );
subStartMD += subSegmentLength;
subEndMD += std::min( subSegmentLength, endMD );
}
return subSegmentMDPairs;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
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 );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateFishbonesMswExportInfo(
const RimEclipseCase* caseToApply,
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();
generateFishbonesMswExportInfo( caseToApply,
wellPath,
initialMD,
cellIntersections,
fishbonesSubs,
enableSegmentSplitting,
exportInfo,
branch );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateFishbonesMswExportInfo(
const RimEclipseCase* caseToApply,
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(), caseToApply );
auto mswParameters = wellPath->fishbonesCollection()->mswParameters();
bool foundSubGridIntersections = false;
// Create a dummy perforation interval
RimPerforationInterval perfInterval;
perfInterval.setStartAndEndMD( wellPath->fishbonesCollection()->startMD(), wellPath->fishbonesCollection()->endMD() );
createWellPathSegments( branch, filteredIntersections, { &perfInterval }, wellPath, -1, caseToApply, &foundSubGridIntersections );
double maxSegmentLength = enableSegmentSplitting ? mswParameters->maxSegmentLength()
: std::numeric_limits<double>::infinity();
double subStartMD = wellPath->fishbonesCollection()->startMD();
double subStartTVD = RicWellPathExportMswCompletionsImpl::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 );
}
for ( const auto& sub : subAndLateralIndices )
{
double subEndMD = subs->measuredDepth( sub.first );
double subEndTVD = RicWellPathExportMswCompletionsImpl::tvdFromMeasuredDepth( branch->wellPath(), subEndMD );
{
auto segment = std::make_unique<RicMswSegment>( subs->generatedName(),
subStartMD,
subEndMD,
subStartTVD,
subEndTVD,
sub.first );
segment->setEffectiveDiameter( subs->effectiveDiameter( unitSystem ) );
segment->setHoleDiameter( subs->holeDiameter( unitSystem ) );
segment->setOpenHoleRoughnessFactor( subs->openHoleRoughnessFactor( unitSystem ) );
segment->setSkinFactor( subs->skinFactor() );
segment->setSourcePdmObject( subs );
// Add completion for ICD
auto icdCompletion =
std::make_unique<RicMswFishbonesICD>( QString( "ICD" ), wellPath, subEndMD, subEndTVD, nullptr );
auto icdSegment =
std::make_unique<RicMswSegment>( "ICD segment", subEndMD, subEndMD + 0.1, subEndTVD, subEndTVD, sub.first );
icdCompletion->setFlowCoefficient( subs->icdFlowCoefficient() );
double icdOrificeRadius = subs->icdOrificeDiameter( unitSystem ) / 2;
icdCompletion->setArea( icdOrificeRadius * icdOrificeRadius * cvf::PI_D * subs->icdCount() );
icdCompletion->addSegment( std::move( icdSegment ) );
segment->addCompletion( std::move( icdCompletion ) );
for ( auto lateralIndex : sub.second )
{
QString label = QString( "Lateral %1" ).arg( lateralIndex );
segment->addCompletion(
std::make_unique<RicMswFishbones>( label, wellPath, subEndMD, subEndTVD, lateralIndex ) );
}
assignFishbonesLateralIntersections( caseToApply,
branch->wellPath(),
subs,
segment.get(),
&foundSubGridIntersections,
maxSegmentLength );
exportInfo->mainBoreBranch()->addSegment( std::move( segment ) );
}
subStartMD = subEndMD;
subStartTVD = subEndTVD;
}
}
exportInfo->setHasSubGridIntersections( exportInfo->hasSubGridIntersections() || foundSubGridIntersections );
exportInfo->mainBoreBranch()->sortSegments();
if ( auto wellPathGroup = dynamic_cast<const RimWellPathGroup*>( wellPath ); wellPathGroup != nullptr )
{
auto initialChildMD = wellPathGroup->uniqueEndMD();
auto initialChildTVD = tvdFromMeasuredDepth( wellPathGroup, initialMD );
for ( auto childWellPath : wellPathGroup->childWellPaths() )
{
auto childCellIntersections =
generateCellSegments( caseToApply, childWellPath, mswParameters, &initialChildMD );
auto childBranch =
std::make_unique<RicMswBranch>( childWellPath->name(), childWellPath, initialChildMD, initialChildTVD );
if ( wellPathGroup->outletValve() )
{
childBranch = RicMswValve::createExportValve( QString( "%1 valve for %2" )
.arg( wellPathGroup->outletValve()->componentLabel() )
.arg( childWellPath->name() ),
childWellPath,
initialChildMD,
initialChildTVD,
wellPathGroup->outletValve() );
auto dummySegment = std::make_unique<
RicMswSegment>( QString( "%1 segment" ).arg( wellPathGroup->outletValve()->componentLabel() ),
initialChildMD,
initialChildMD + 0.1,
initialChildTVD,
RicWellPathExportMswCompletionsImpl::tvdFromMeasuredDepth( wellPath,
initialChildMD + 0.1 ) );
childBranch->addSegment( std::move( dummySegment ) );
}
generateFishbonesMswExportInfo( caseToApply,
childWellPath,
initialChildMD,
childCellIntersections,
enableSegmentSplitting,
exportInfo,
childBranch.get() );
branch->addChildBranch( std::move( childBranch ) );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RicMswExportInfo RicWellPathExportMswCompletionsImpl::generateFracturesMswExportInfo( RimEclipseCase* caseToApply,
const RimWellPath* wellPath )
{
std::vector<RimWellPathFracture*> fractures = wellPath->fractureCollection()->activeFractures();
return generateFracturesMswExportInfo( caseToApply, wellPath, fractures );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RicMswExportInfo
RicWellPathExportMswCompletionsImpl::generateFracturesMswExportInfo( RimEclipseCase* caseToApply,
const RimWellPath* wellPath,
const std::vector<RimWellPathFracture*>& fractures )
{
const RigMainGrid* grid = caseToApply->eclipseCaseData()->mainGrid();
const RigActiveCellInfo* activeCellInfo =
caseToApply->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
RiaDefines::EclipseUnitSystem unitSystem = caseToApply->eclipseCaseData()->unitsType();
auto wellPathGeometry = wellPath->wellPathGeometry();
CVF_ASSERT( wellPathGeometry );
const std::vector<cvf::Vec3d>& coords = wellPathGeometry->wellPathPoints();
const std::vector<double>& mds = wellPathGeometry->measuredDepths();
CVF_ASSERT( !coords.empty() && !mds.empty() );
std::vector<WellPathCellIntersectionInfo> intersections =
RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath( caseToApply->eclipseCaseData(),
wellPath->name(),
coords,
mds );
auto mswParameters = wellPath->fractureCollection()->mswParameters();
double initialMD = 0.0;
if ( mswParameters->referenceMDType() == RimMswCompletionParameters::MANUAL_REFERENCE_MD )
{
initialMD = mswParameters->manualReferenceMD();
}
else
{
for ( const WellPathCellIntersectionInfo& intersection : intersections )
{
if ( activeCellInfo->isActive( intersection.globCellIndex ) )
{
initialMD = intersection.startMD;
break;
}
}
double startOfFirstCompletion = std::numeric_limits<double>::infinity();
{
for ( auto* fracture : fractures )
{
if ( fracture->isEnabled() && fracture->startMD() < startOfFirstCompletion )
{
startOfFirstCompletion = fracture->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 );
}
RicMswExportInfo exportInfo( wellPath,
unitSystem,
initialMD,
mswParameters->lengthAndDepth().text(),
mswParameters->pressureDrop().text() );
exportInfo.setLinerDiameter( mswParameters->linerDiameter( unitSystem ) );
exportInfo.setRoughnessFactor( mswParameters->roughnessFactor( unitSystem ) );
bool foundSubGridIntersections = false;
// Main bore
int mainBoreSegment = 1;
for ( const auto& cellIntInfo : intersections )
{
size_t localGridIdx = 0u;
const RigGridBase* localGrid =
grid->gridAndGridLocalIdxFromGlobalCellIdx( cellIntInfo.globCellIndex, &localGridIdx );
QString gridName;
if ( localGrid != grid )
{
gridName = QString::fromStdString( localGrid->gridName() );
foundSubGridIntersections = true;
}
size_t i = 0u, j = 0u, k = 0u;
localGrid->ijkFromCellIndex( localGridIdx, &i, &j, &k );
auto segment = std::make_unique<RicMswSegment>( "Main stem segment",
cellIntInfo.startMD,
cellIntInfo.endMD,
cellIntInfo.startTVD(),
cellIntInfo.endTVD() );
// 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;
}
if ( cvf::Math::valueInRange( fractureStartMD, cellIntInfo.startMD, cellIntInfo.endMD ) )
{
std::vector<RigCompletionData> completionData =
RicExportFractureCompletionsImpl::generateCompdatValues( caseToApply,
wellPath->completionSettings()->wellNameForExport(),
wellPath->wellPathGeometry(),
{ fracture },
nullptr,
nullptr );
assignFractureCompletionsToCellSegment( caseToApply,
wellPath,
fracture,
completionData,
segment.get(),
&foundSubGridIntersections );
}
}
exportInfo.mainBoreBranch()->addSegment( std::move( segment ) );
}
exportInfo.setHasSubGridIntersections( foundSubGridIntersections );
exportInfo.mainBoreBranch()->sortSegments();
int branchNumber = 1;
assignBranchNumbersToBranch( caseToApply, &exportInfo, exportInfo.mainBoreBranch(), &branchNumber );
return exportInfo;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
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();
if ( auto wellPathGroup = dynamic_cast<const RimWellPathGroup*>( wellPath ); wellPathGroup != nullptr )
{
auto mswParameters = wellPath->perforationIntervalCollection()->mswParameters();
if ( !mswParameters )
{
RiaLogging::error( "generatePerforationsMswExportInfo: No mswParameters object found, aborting export" );
}
else
{
auto initialChildMD = wellPathGroup->uniqueEndMD();
auto initialChildTVD = -wellPathGroup->wellPathGeometry()->interpolatedPointAlongWellPath( initialMD ).z();
for ( auto childWellPath : wellPathGroup->childWellPaths() )
{
auto childCellIntersections =
generateCellSegments( eclipseCase, childWellPath, mswParameters, &initialChildMD );
auto childBranch =
std::make_unique<RicMswBranch>( childWellPath->name(), childWellPath, initialChildMD, initialChildTVD );
if ( wellPathGroup->outletValve() )
{
childBranch = RicMswValve::createExportValve( QString( "%1 valve for %2" )
.arg( wellPathGroup->outletValve()->componentLabel() )
.arg( childWellPath->name() ),
childWellPath,
initialChildMD,
initialChildTVD,
wellPathGroup->outletValve() );
auto dummySegment = std::make_unique<
RicMswSegment>( QString( "%1 segment" ).arg( wellPathGroup->outletValve()->componentLabel() ),
initialChildMD,
initialChildMD + 0.1,
initialChildTVD,
RicWellPathExportMswCompletionsImpl::tvdFromMeasuredDepth( wellPath,
initialChildMD + 0.1 ) );
childBranch->addSegment( std::move( dummySegment ) );
}
if ( generatePerforationsMswExportInfo( eclipseCase,
childWellPath,
timeStep,
initialChildMD,
childCellIntersections,
exportInfo,
childBranch.get() ) )
{
branch->addChildBranch( std::move( childBranch ) );
}
}
}
}
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 );
std::vector<WellPathCellIntersectionInfo> continuousIntersections =
RigWellPathIntersectionTools::buildContinuousIntersections( allIntersections, mainGrid );
if ( mswParameters->referenceMDType() == RimMswCompletionParameters::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 =
RicWellPathExportMswCompletionsImpl::tvdFromMeasuredDepth( wellPath, exportStartMD );
double exportEndTVD =
RicWellPathExportMswCompletionsImpl::tvdFromMeasuredDepth( wellPath, exportEndMD );
double overlapStartTVD =
RicWellPathExportMswCompletionsImpl::tvdFromMeasuredDepth( wellPath, overlapStart );
double overlapEndTVD =
RicWellPathExportMswCompletionsImpl::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 ) );
ICV->setFlowCoefficient( valve->flowCoefficient() );
double orificeRadius = valve->orificeDiameter( unitSystem ) / 2;
ICV->setArea( orificeRadius * orificeRadius * cvf::PI_D );
}
}
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::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::writeWelsegsSegment( RicMswSegment* segment,
const RicMswSegment* previousSegment,
RifTextDataTableFormatter& formatter,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
gsl::not_null<RicMswBranch*> branch,
int* segmentNumber )
{
CVF_ASSERT( segment && segmentNumber );
double startMD = segment->startMD();
double endMD = segment->endMD();
std::vector<std::pair<double, double>> segments = createSubSegmentMDPairs( startMD, endMD, maxSegmentLength );
CVF_ASSERT( branch->wellPath() );
auto wellPathGeometry = branch->wellPath()->wellPathGeometry();
CVF_ASSERT( wellPathGeometry );
double prevOutMD = branch->startMD();
double prevOutTVD = branch->startTVD();
if ( previousSegment )
{
prevOutMD = previousSegment->outputMD();
prevOutTVD = previousSegment->outputTVD();
}
auto outletSegment = previousSegment;
for ( const auto& [subStartMD, subEndMD] : segments )
{
double depth = 0;
double length = 0;
double midPointMD = 0.5 * ( subStartMD + subEndMD );
double midPointTVD = tvdFromMeasuredDepth( branch->wellPath(), midPointMD );
if ( exportInfo.lengthAndDepthText() == QString( "INC" ) )
{
depth = midPointTVD - prevOutTVD;
length = midPointMD - prevOutMD;
}
else
{
depth = midPointTVD;
length = midPointMD;
}
segment->setOutputMD( midPointMD );
segment->setOutputTVD( midPointTVD );
segment->setSegmentNumber( *segmentNumber );
formatter.add( *segmentNumber ).add( *segmentNumber );
formatter.add( branch->branchNumber() );
if ( outletSegment )
formatter.add( outletSegment->segmentNumber() );
else
formatter.add( 1 );
formatter.add( length );
formatter.add( depth );
formatter.add( exportInfo.linerDiameter() );
formatter.add( exportInfo.roughnessFactor() );
formatter.rowCompleted();
( *segmentNumber )++;
outletSegment = segment;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::writeValveWelsegsSegment( const RicMswSegment* outletSegment,
RicMswValve* valve,
RifTextDataTableFormatter& formatter,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
int* segmentNumber )
{
CVF_ASSERT( valve );
if ( !valve->isValid() ) return;
CVF_ASSERT( !valve->label().isEmpty() );
CVF_ASSERT( valve->wellPath() );
formatter.addOptionalComment( valve->label() );
auto segments = valve->segments();
auto subSegment = segments.front();
subSegment->setSegmentNumber( *segmentNumber );
double startMD = subSegment->startMD();
double endMD = subSegment->endMD();
double midPointMD = 0.5 * ( startMD + endMD );
double midPointTVD = tvdFromMeasuredDepth( valve->wellPath(), midPointMD );
subSegment->setOutputMD( midPointMD );
subSegment->setOutputTVD( midPointTVD );
std::vector<std::pair<double, double>> splitSegments = createSubSegmentMDPairs( startMD, endMD, maxSegmentLength );
auto wellPathGeometry = valve->wellPath()->wellPathGeometry();
CVF_ASSERT( wellPathGeometry );
for ( const auto& [subStartMD, subEndMD] : splitSegments )
{
int subSegmentNumber = ( *segmentNumber )++;
double subStartTVD = tvdFromMeasuredDepth( valve->wellPath(), subStartMD );
double subEndTVD = tvdFromMeasuredDepth( valve->wellPath(), subEndMD );
double depth = 0;
double length = 0;
if ( exportInfo.lengthAndDepthText() == QString( "INC" ) )
{
depth = subEndTVD - subStartTVD;
length = subEndMD - subStartMD;
}
else
{
depth = subEndTVD;
length = subEndMD;
}
formatter.add( subSegmentNumber );
formatter.add( subSegmentNumber );
formatter.add( valve->branchNumber() );
formatter.add( outletSegment->segmentNumber() );
formatter.add( length );
formatter.add( depth );
formatter.add( exportInfo.linerDiameter() );
formatter.add( exportInfo.roughnessFactor() );
formatter.rowCompleted();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::writeCompletionWelsegsSegments( gsl::not_null<const RicMswSegment*> outletSegment,
gsl::not_null<const RicMswCompletion*> completion,
RifTextDataTableFormatter& formatter,
RicMswExportInfo& exportInfo,
double maxSegmentLength,
int* segmentNumber )
{
writeWelsegsCompletionCommentHeader( formatter, completion->completionType() );
if ( completion->completionType() == RigCompletionData::FISHBONES )
{
formatter.addOptionalComment(
QString( "Sub index %1 - %2" ).arg( outletSegment->subIndex() ).arg( completion->label() ) );
}
else if ( completion->completionType() == RigCompletionData::FRACTURE )
{
formatter.addOptionalComment(
QString( "%1 connected to segment %2" ).arg( completion->label() ).arg( outletSegment->segmentNumber() ) );
}
CVF_ASSERT( completion->wellPath() );
int outletSegmentNumber = outletSegment->segmentNumber();
for ( auto segment : completion->segments() )
{
double startMD = segment->startMD();
double endMD = segment->endMD();
std::vector<std::pair<double, double>> splitSegments = createSubSegmentMDPairs( startMD, endMD, maxSegmentLength );
for ( const auto& [subStartMD, subEndMD] : splitSegments )
{
int subSegmentNumber = ( *segmentNumber )++;
double subStartTVD = tvdFromMeasuredDepth( completion->wellPath(), subStartMD );
double subEndTVD = tvdFromMeasuredDepth( completion->wellPath(), subEndMD );
double depth = 0;
double length = 0;
if ( exportInfo.lengthAndDepthText() == QString( "INC" ) )
{
depth = subEndTVD - subStartTVD;
length = subEndMD - subStartMD;
}
else
{
depth = subEndTVD;
length = subEndMD;
}
formatter.add( subSegmentNumber );
formatter.add( subSegmentNumber );
formatter.add( completion->branchNumber() );
formatter.add( outletSegmentNumber );
formatter.add( length );
formatter.add( depth );
formatter.add( outletSegment->effectiveDiameter() );
formatter.add( outletSegment->openHoleRoughnessFactor() );
formatter.rowCompleted();
outletSegmentNumber = subSegmentNumber;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
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::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* caseToApply,
const RimWellPath* wellPath,
const RimFishbones* fishbonesSubs,
gsl::not_null<RicMswSegment*> segment,
bool* foundSubGridIntersections,
double maxSegmentLength )
{
CVF_ASSERT( foundSubGridIntersections != nullptr );
const RigMainGrid* grid = caseToApply->eclipseCaseData()->mainGrid();
for ( auto completion : segment->completions() )
{
if ( completion->completionType() != RigCompletionData::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( caseToApply->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 localGridIdx = 0u;
const RigGridBase* localGrid =
grid->gridAndGridLocalIdxFromGlobalCellIdx( cellIntInfo.globCellIndex, &localGridIdx );
QString gridName;
if ( localGrid != grid )
{
gridName = QString::fromStdString( localGrid->gridName() );
*foundSubGridIntersections = true;
}
size_t i = 0u, j = 0u, k = 0u;
localGrid->ijkFromCellIndex( localGridIdx, &i, &j, &k );
auto subSegment = std::make_unique<RicMswSegment>( "Sub segment",
previousExitMD,
cellIntInfo.endMD,
previousExitTVD,
cellIntInfo.endTVD() );
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* caseToApply,
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();
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* caseToApply,
gsl::not_null<RicMswSegment*> segment,
gsl::not_null<int*> branchNumber )
{
for ( auto completion : segment->completions() )
{
if ( completion->completionType() == RigCompletionData::PERFORATION )
{
completion->setBranchNumber( *branchNumber );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignBranchNumbersToOtherCompletions( const RimEclipseCase* caseToApply,
gsl::not_null<RicMswSegment*> segment,
gsl::not_null<int*> branchNumber )
{
for ( auto completion : segment->completions() )
{
if ( completion->completionType() != RigCompletionData::PERFORATION )
{
completion->setBranchNumber( ++( *branchNumber ) );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignBranchNumbersToBranch( const RimEclipseCase* caseToApply,
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( caseToApply, segment, branchNumber );
}
// Assign other completions with an incremented branch number
for ( auto segment : branch->segments() )
{
assignBranchNumbersToOtherCompletions( caseToApply, segment, branchNumber );
}
( *branchNumber )++;
for ( auto childBranch : branch->branches() )
{
assignBranchNumbersToBranch( caseToApply, exportInfo, childBranch, branchNumber );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RicWellPathExportMswCompletionsImpl::tvdFromMeasuredDepth( gsl::not_null<const RimWellPath*> wellPath,
double measuredDepth )
{
auto wellPathGeometry = wellPath->wellPathGeometry();
CVF_ASSERT( wellPathGeometry );
double tvdValue = -wellPathGeometry->interpolatedPointAlongWellPath( measuredDepth ).z();
return tvdValue;
}