mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
1711 lines
89 KiB
C++
1711 lines
89 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2017 Statoil 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 "RicWellPathExportCompletionDataFeatureImpl.h"
|
|
|
|
#include "RiaEclipseUnitTools.h"
|
|
#include "RiaFilePathTools.h"
|
|
#include "RiaFractureDefines.h"
|
|
#include "RiaLogging.h"
|
|
#include "RiaPreferencesSystem.h"
|
|
#include "RiaWeightedMeanCalculator.h"
|
|
|
|
#include "ExportCommands/RicExportLgrFeature.h"
|
|
#include "RicExportCompletionDataSettingsUi.h"
|
|
#include "RicExportFeatureImpl.h"
|
|
#include "RicExportFractureCompletionsImpl.h"
|
|
#include "RicFishbonesTransmissibilityCalculationFeatureImp.h"
|
|
#include "RicWellPathExportCompletionsFileTools.h"
|
|
#include "RicWellPathExportMswCompletionsImpl.h"
|
|
#include "RicWellPathFractureReportItem.h"
|
|
#include "RicWellPathFractureTextReportFeatureImpl.h"
|
|
|
|
#include "RifTextDataTableFormatter.h"
|
|
#include "RifThermalToStimPlanFractureXmlOutput.h"
|
|
|
|
#include "RigActiveCellInfo.h"
|
|
#include "RigCaseCellResultsData.h"
|
|
#include "RigEclipseCaseData.h"
|
|
#include "RigEclipseResultAddress.h"
|
|
#include "RigMainGrid.h"
|
|
#include "RigPerforationTransmissibilityEquations.h"
|
|
#include "RigResultAccessorFactory.h"
|
|
#include "RigTransmissibilityEquations.h"
|
|
#include "RigVirtualPerforationTransmissibilities.h"
|
|
#include "RigWellLogExtractionTools.h"
|
|
#include "RigWellLogExtractor.h"
|
|
#include "RigWellPath.h"
|
|
#include "RigWellPathIntersectionTools.h"
|
|
|
|
#include "RimFileWellPath.h"
|
|
#include "RimFishbones.h"
|
|
#include "RimFishbonesCollection.h"
|
|
#include "RimFractureTemplate.h"
|
|
#include "RimNonDarcyPerforationParameters.h"
|
|
#include "RimPerforationCollection.h"
|
|
#include "RimPerforationInterval.h"
|
|
#include "RimProject.h"
|
|
#include "RimSimWellInView.h"
|
|
#include "RimThermalFractureTemplate.h"
|
|
#include "RimWellPath.h"
|
|
#include "RimWellPathCollection.h"
|
|
#include "RimWellPathCompletions.h"
|
|
#include "RimWellPathFracture.h"
|
|
#include "RimWellPathFractureCollection.h"
|
|
#include "RimWellPathValve.h"
|
|
|
|
#include "Riu3DMainWindowTools.h"
|
|
#include "RiuMainWindow.h"
|
|
|
|
#include "cafPdmUiPropertyViewDialog.h"
|
|
#include "cafProgressInfo.h"
|
|
#include "cafSelectionManager.h"
|
|
#include "cafUtils.h"
|
|
|
|
#include "cvfPlane.h"
|
|
|
|
#include <QDir>
|
|
|
|
#include <map>
|
|
#include <set>
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportCompletionDataFeatureImpl::exportCompletions( const std::vector<RimWellPath*>& wellPaths,
|
|
const std::vector<RimSimWellInView*>& simWells,
|
|
const RicExportCompletionDataSettingsUi& exportSettings )
|
|
{
|
|
if ( exportSettings.caseToApply() == nullptr )
|
|
{
|
|
RiaLogging::error( "Export Completions Data: Cannot export completions data a valid Eclipse case" );
|
|
return;
|
|
}
|
|
|
|
// Ensure that the case is open. This will enable export without any open views.
|
|
// https://github.com/OPM/ResInsight/issues/11134
|
|
exportSettings.caseToApply()->ensureReservoirCaseIsOpen();
|
|
|
|
if ( exportSettings.caseToApply()->eclipseCaseData() == nullptr )
|
|
{
|
|
RiaLogging::error( "Export Completions Data: No data available for Eclipse Case" );
|
|
return;
|
|
}
|
|
|
|
if ( exportSettings.customFileName().isEmpty() )
|
|
{
|
|
QDir folder( exportSettings.folder );
|
|
if ( !folder.exists() )
|
|
{
|
|
QString txt = QString( "The path '%1' does not exist. Aborting export." ).arg( exportSettings.folder );
|
|
RiaLogging::errorInMessageBox( Riu3DMainWindowTools::mainWindowWidget(), "Export", txt );
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
exportCarfinForTemporaryLgrs( exportSettings.caseToApply(), exportSettings.folder );
|
|
|
|
{
|
|
std::vector<RicWellPathFractureReportItem> fractureDataReportItems;
|
|
std::unique_ptr<QTextStream> fractureTransmissibilityExportInformationStream = nullptr;
|
|
QFile fractureTransmissibilityExportInformationFile;
|
|
|
|
RiaPreferencesSystem* prefs = RiaPreferencesSystem::current();
|
|
if ( prefs->includeFractureDebugInfoFile() )
|
|
{
|
|
QDir outputDir = QDir( exportSettings.folder );
|
|
if ( !outputDir.mkpath( "." ) )
|
|
{
|
|
QString errMsg = QString( "Could not create export folder: %1" ).arg( exportSettings.folder );
|
|
RiaLogging::error( errMsg );
|
|
return;
|
|
}
|
|
|
|
QString fractureTransmisibillityExportInformationPath =
|
|
QDir( exportSettings.folder ).absoluteFilePath( "FractureTransmissibilityExportInformation" );
|
|
|
|
fractureTransmissibilityExportInformationFile.setFileName( fractureTransmisibillityExportInformationPath );
|
|
if ( !fractureTransmissibilityExportInformationFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
|
|
{
|
|
RiaLogging::error(
|
|
QString( "Export Completions Data: Could not open the file: %1" ).arg( fractureTransmisibillityExportInformationPath ) );
|
|
}
|
|
else
|
|
{
|
|
fractureTransmissibilityExportInformationStream =
|
|
std::make_unique<QTextStream>( &fractureTransmissibilityExportInformationFile );
|
|
}
|
|
|
|
// Extra debugging for thermal fractures
|
|
if ( exportSettings.includeFractures() )
|
|
{
|
|
for ( RimWellPath* wellPath : wellPaths )
|
|
{
|
|
std::vector<RimWellPath*> allWellPathLaterals = wellPath->allWellPathLaterals();
|
|
for ( auto wellPathLateral : allWellPathLaterals )
|
|
{
|
|
for ( auto& frac : wellPathLateral->fractureCollection()->activeFractures() )
|
|
{
|
|
if ( RimThermalFractureTemplate* fractureTemplate =
|
|
dynamic_cast<RimThermalFractureTemplate*>( frac->fractureTemplate() ) )
|
|
{
|
|
QString name = frac->name();
|
|
QString fractureXmlPath = QDir( exportSettings.folder ).absoluteFilePath( name + ".xml" );
|
|
|
|
RifThermalToStimPlanFractureXmlOutput::writeToFile( fractureTemplate, fractureXmlPath );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<RigCompletionData> completions;
|
|
|
|
{
|
|
caf::ProgressInfo progress( wellPaths.size(), "Extracting Completion Data For Well Paths" );
|
|
|
|
for ( RimWellPath* wellPath : wellPaths )
|
|
{
|
|
std::vector<RimWellPath*> allWellPathLaterals;
|
|
if ( wellPath->unitSystem() == exportSettings.caseToApply->eclipseCaseData()->unitsType() )
|
|
{
|
|
allWellPathLaterals = wellPath->allWellPathLaterals();
|
|
}
|
|
else
|
|
{
|
|
int caseId = exportSettings.caseToApply->caseId();
|
|
QString format = QString( "Unit systems for well path \"%1\" must match unit system of chosen eclipse case \"%2\"" );
|
|
QString errMsg = format.arg( wellPath->name() ).arg( caseId );
|
|
RiaLogging::error( errMsg );
|
|
}
|
|
|
|
std::map<size_t, std::vector<RigCompletionData>> completionsPerEclipseCellAllCompletionTypes;
|
|
std::map<size_t, std::vector<RigCompletionData>> completionsPerEclipseCellFishbones;
|
|
std::map<size_t, std::vector<RigCompletionData>> completionsPerEclipseCellFracture;
|
|
std::map<size_t, std::vector<RigCompletionData>> completionsPerEclipseCellPerforations;
|
|
|
|
for ( auto wellPathLateral : allWellPathLaterals )
|
|
{
|
|
// Skip well paths that are not enabled, no export of WELSEGS or COMPDAT
|
|
// https://github.com/OPM/ResInsight/issues/10754
|
|
//
|
|
if ( !wellPathLateral->isEnabled() ) continue;
|
|
|
|
// Generate completion data
|
|
|
|
if ( exportSettings.includePerforations )
|
|
{
|
|
std::vector<RigCompletionData> perforationCompletionData =
|
|
generatePerforationsCompdatValues( wellPathLateral,
|
|
wellPathLateral->perforationIntervalCollection()->perforations(),
|
|
exportSettings );
|
|
|
|
appendCompletionData( &completionsPerEclipseCellAllCompletionTypes, perforationCompletionData );
|
|
appendCompletionData( &completionsPerEclipseCellPerforations, perforationCompletionData );
|
|
}
|
|
|
|
if ( exportSettings.includeFishbones )
|
|
{
|
|
std::vector<RigCompletionData> fishbonesCompletionData =
|
|
RicFishbonesTransmissibilityCalculationFeatureImp::generateFishboneCompdatValuesUsingAdjustedCellVolume( wellPathLateral,
|
|
exportSettings );
|
|
|
|
appendCompletionData( &completionsPerEclipseCellAllCompletionTypes, fishbonesCompletionData );
|
|
appendCompletionData( &completionsPerEclipseCellFishbones, fishbonesCompletionData );
|
|
}
|
|
|
|
if ( exportSettings.includeFractures() )
|
|
{
|
|
// If no report is wanted, set reportItems = nullptr
|
|
std::vector<RicWellPathFractureReportItem>* reportItems = &fractureDataReportItems;
|
|
|
|
std::vector<RigCompletionData> fractureCompletionData = RicExportFractureCompletionsImpl::
|
|
generateCompdatValuesForWellPath( wellPathLateral,
|
|
exportSettings.caseToApply(),
|
|
reportItems,
|
|
fractureTransmissibilityExportInformationStream.get(),
|
|
RicExportFractureCompletionsImpl::
|
|
PressureDepletionParameters( exportSettings.performTransScaling(),
|
|
exportSettings.transScalingTimeStep(),
|
|
exportSettings.transScalingWBHPSource(),
|
|
exportSettings.transScalingWBHP() ) );
|
|
|
|
appendCompletionData( &completionsPerEclipseCellAllCompletionTypes, fractureCompletionData );
|
|
appendCompletionData( &completionsPerEclipseCellFracture, fractureCompletionData );
|
|
}
|
|
}
|
|
|
|
if ( exportSettings.reportCompletionsTypesIndividually() )
|
|
{
|
|
for ( auto& data : completionsPerEclipseCellFracture )
|
|
{
|
|
completions.push_back( combineEclipseCellCompletions( data.second, exportSettings ) );
|
|
}
|
|
|
|
for ( auto& data : completionsPerEclipseCellFishbones )
|
|
{
|
|
completions.push_back( combineEclipseCellCompletions( data.second, exportSettings ) );
|
|
}
|
|
|
|
for ( auto& data : completionsPerEclipseCellPerforations )
|
|
{
|
|
completions.push_back( combineEclipseCellCompletions( data.second, exportSettings ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( auto& data : completionsPerEclipseCellAllCompletionTypes )
|
|
{
|
|
completions.push_back( combineEclipseCellCompletions( data.second, exportSettings ) );
|
|
}
|
|
}
|
|
|
|
progress.incrementProgress();
|
|
}
|
|
}
|
|
|
|
for ( auto simWell : simWells )
|
|
{
|
|
std::map<size_t, std::vector<RigCompletionData>> completionsPerEclipseCell;
|
|
|
|
std::vector<RigCompletionData> fractureCompletionData = RicExportFractureCompletionsImpl::
|
|
generateCompdatValuesForSimWell( exportSettings.caseToApply(),
|
|
simWell,
|
|
fractureTransmissibilityExportInformationStream.get(),
|
|
RicExportFractureCompletionsImpl::
|
|
PressureDepletionParameters( exportSettings.performTransScaling(),
|
|
exportSettings.transScalingTimeStep(),
|
|
exportSettings.transScalingWBHPSource(),
|
|
exportSettings.transScalingWBHP() ) );
|
|
|
|
appendCompletionData( &completionsPerEclipseCell, fractureCompletionData );
|
|
|
|
for ( auto& data : completionsPerEclipseCell )
|
|
{
|
|
completions.push_back( combineEclipseCellCompletions( data.second, exportSettings ) );
|
|
}
|
|
}
|
|
|
|
const QString eclipseCaseName = exportSettings.caseToApply->caseUserDescription();
|
|
|
|
if ( exportSettings.fileSplit == RicExportCompletionDataSettingsUi::ExportSplit::UNIFIED_FILE )
|
|
{
|
|
QString fileName = exportSettings.customFileName();
|
|
if ( fileName.isEmpty() )
|
|
{
|
|
fileName = QString( "UnifiedCompletions_%1" ).arg( eclipseCaseName );
|
|
}
|
|
|
|
sortAndExportCompletionsToFile( exportSettings.caseToApply,
|
|
exportSettings.folder,
|
|
fileName,
|
|
completions,
|
|
fractureDataReportItems,
|
|
exportSettings.compdatExport,
|
|
exportSettings.exportDataSourceAsComment(),
|
|
exportSettings.exportWelspec() );
|
|
}
|
|
else if ( exportSettings.fileSplit == RicExportCompletionDataSettingsUi::ExportSplit::SPLIT_ON_WELL )
|
|
{
|
|
for ( auto wellPath : wellPaths )
|
|
{
|
|
std::vector<RigCompletionData> completionsForWell;
|
|
for ( const auto& completion : completions )
|
|
{
|
|
if ( wellPath == topLevelWellPath( completion ) )
|
|
{
|
|
completionsForWell.push_back( completion );
|
|
}
|
|
}
|
|
|
|
if ( completionsForWell.empty() ) continue;
|
|
|
|
std::vector<RicWellPathFractureReportItem> reportItemsForWell;
|
|
for ( const auto& fracItem : fractureDataReportItems )
|
|
{
|
|
if ( fracItem.wellPathNameForExport() == wellPath->completionSettings()->wellNameForExport() )
|
|
{
|
|
reportItemsForWell.push_back( fracItem );
|
|
}
|
|
}
|
|
|
|
QString fileName = QString( "%1_UnifiedCompletions_%2" ).arg( wellPath->name() ).arg( eclipseCaseName );
|
|
sortAndExportCompletionsToFile( exportSettings.caseToApply,
|
|
exportSettings.folder,
|
|
fileName,
|
|
completionsForWell,
|
|
reportItemsForWell,
|
|
exportSettings.compdatExport,
|
|
exportSettings.exportDataSourceAsComment(),
|
|
exportSettings.exportWelspec() );
|
|
}
|
|
}
|
|
else if ( exportSettings.fileSplit == RicExportCompletionDataSettingsUi::ExportSplit::SPLIT_ON_WELL_AND_COMPLETION_TYPE )
|
|
{
|
|
std::vector<RigCompletionData::CompletionType> completionTypes;
|
|
completionTypes.push_back( RigCompletionData::CompletionType::FISHBONES );
|
|
completionTypes.push_back( RigCompletionData::CompletionType::FRACTURE );
|
|
completionTypes.push_back( RigCompletionData::CompletionType::PERFORATION );
|
|
|
|
for ( const auto& completionType : completionTypes )
|
|
{
|
|
for ( auto wellPath : wellPaths )
|
|
{
|
|
std::vector<RigCompletionData> completionsForWell;
|
|
for ( const auto& completion : completions )
|
|
{
|
|
if ( completionType == completion.completionType() )
|
|
{
|
|
if ( wellPath == topLevelWellPath( completion ) )
|
|
{
|
|
completionsForWell.push_back( completion );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( completionsForWell.empty() ) continue;
|
|
|
|
std::vector<RicWellPathFractureReportItem> reportItemsForWell;
|
|
if ( completionType == RigCompletionData::CompletionType::FRACTURE )
|
|
{
|
|
for ( const auto& fracItem : fractureDataReportItems )
|
|
{
|
|
if ( fracItem.wellPathNameForExport() == wellPath->completionSettings()->wellNameForExport() )
|
|
{
|
|
reportItemsForWell.push_back( fracItem );
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
QString completionTypeText;
|
|
if ( completionType == RigCompletionData::CompletionType::FISHBONES ) completionTypeText = "Fishbones";
|
|
if ( completionType == RigCompletionData::CompletionType::FRACTURE ) completionTypeText = "Fracture";
|
|
if ( completionType == RigCompletionData::CompletionType::PERFORATION ) completionTypeText = "Perforation";
|
|
|
|
QString fileName = QString( "%1_%2_%3" ).arg( wellPath->name() ).arg( completionTypeText ).arg( eclipseCaseName );
|
|
sortAndExportCompletionsToFile( exportSettings.caseToApply,
|
|
exportSettings.folder,
|
|
fileName,
|
|
completionsForWell,
|
|
reportItemsForWell,
|
|
exportSettings.compdatExport,
|
|
exportSettings.exportDataSourceAsComment(),
|
|
exportSettings.exportWelspec() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Export sim wells
|
|
if ( exportSettings.fileSplit == RicExportCompletionDataSettingsUi::ExportSplit::SPLIT_ON_WELL ||
|
|
exportSettings.fileSplit == RicExportCompletionDataSettingsUi::ExportSplit::SPLIT_ON_WELL_AND_COMPLETION_TYPE )
|
|
{
|
|
for ( auto simWell : simWells )
|
|
{
|
|
std::vector<RigCompletionData> wellCompletions;
|
|
for ( const auto& completion : completions )
|
|
{
|
|
if ( completion.wellName() == simWell->name() )
|
|
{
|
|
wellCompletions.push_back( completion );
|
|
}
|
|
}
|
|
|
|
if ( wellCompletions.empty() ) continue;
|
|
|
|
QString fileName = QString( "%1_Fractures_%2" ).arg( simWell->name() ).arg( eclipseCaseName );
|
|
sortAndExportCompletionsToFile( exportSettings.caseToApply,
|
|
exportSettings.folder,
|
|
fileName,
|
|
wellCompletions,
|
|
fractureDataReportItems,
|
|
exportSettings.compdatExport,
|
|
exportSettings.exportDataSourceAsComment(),
|
|
exportSettings.exportWelspec() );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( exportSettings.includeMsw )
|
|
{
|
|
RicWellPathExportMswCompletionsImpl::exportWellSegmentsForAllCompletions( exportSettings, wellPaths );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RigCompletionData> RicWellPathExportCompletionDataFeatureImpl::computeStaticCompletionsForWellPath( RimWellPath* wellPath,
|
|
RimEclipseCase* eclipseCase )
|
|
{
|
|
std::vector<RigCompletionData> completionsPerEclipseCell;
|
|
|
|
if ( eclipseCase && eclipseCase->eclipseCaseData() )
|
|
{
|
|
RicExportCompletionDataSettingsUi exportSettings;
|
|
exportSettings.caseToApply = eclipseCase;
|
|
exportSettings.timeStep = 0;
|
|
exportSettings.includeFishbones = true;
|
|
exportSettings.includePerforations = true;
|
|
exportSettings.includeFractures = true;
|
|
|
|
{
|
|
std::vector<RigCompletionData> completionData =
|
|
RicFishbonesTransmissibilityCalculationFeatureImp::generateFishboneCompdatValuesUsingAdjustedCellVolume( wellPath,
|
|
exportSettings );
|
|
|
|
std::copy( completionData.begin(), completionData.end(), std::back_inserter( completionsPerEclipseCell ) );
|
|
}
|
|
|
|
{
|
|
std::vector<RigCompletionData> completionData =
|
|
RicExportFractureCompletionsImpl::generateCompdatValuesForWellPath( wellPath, eclipseCase, nullptr, nullptr );
|
|
|
|
std::copy( completionData.begin(), completionData.end(), std::back_inserter( completionsPerEclipseCell ) );
|
|
}
|
|
}
|
|
|
|
return completionsPerEclipseCell;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RigCompletionData> RicWellPathExportCompletionDataFeatureImpl::computeDynamicCompletionsForWellPath( RimWellPath* wellPath,
|
|
RimEclipseCase* eclipseCase,
|
|
size_t timeStepIndex )
|
|
{
|
|
std::vector<RigCompletionData> completionsPerEclipseCell;
|
|
|
|
if ( eclipseCase && eclipseCase->eclipseCaseData() )
|
|
{
|
|
RicExportCompletionDataSettingsUi exportSettings;
|
|
exportSettings.caseToApply = eclipseCase;
|
|
exportSettings.timeStep = static_cast<int>( timeStepIndex );
|
|
exportSettings.includeFishbones = true;
|
|
exportSettings.includePerforations = true;
|
|
exportSettings.includeFractures = true;
|
|
|
|
completionsPerEclipseCell =
|
|
generatePerforationsCompdatValues( wellPath, wellPath->perforationIntervalCollection()->perforations(), exportSettings );
|
|
}
|
|
|
|
return completionsPerEclipseCell;
|
|
}
|
|
|
|
//==================================================================================================
|
|
///
|
|
//==================================================================================================
|
|
RigCompletionData RicWellPathExportCompletionDataFeatureImpl::combineEclipseCellCompletions( const std::vector<RigCompletionData>& completions,
|
|
const RicExportCompletionDataSettingsUi& settings )
|
|
{
|
|
CVF_ASSERT( !completions.empty() );
|
|
|
|
// For detailed description of how the combined completion data is computed, see
|
|
// https://github.com/OPM/ResInsight/issues/7049
|
|
|
|
const RigCompletionData& firstCompletion = completions[0];
|
|
|
|
const QString& wellName = firstCompletion.wellName();
|
|
const RigCompletionDataGridCell& cellIndexIJK = firstCompletion.completionDataGridCell();
|
|
RigCompletionData::CompletionType completionType = firstCompletion.completionType();
|
|
|
|
RigCompletionData resultCompletion( wellName, cellIndexIJK, firstCompletion.firstOrderingValue() );
|
|
resultCompletion.setSecondOrderingValue( firstCompletion.secondOrderingValue() );
|
|
resultCompletion.setSourcePdmObject( firstCompletion.sourcePdmObject() );
|
|
|
|
RigCompletionData::CellDirection cellDirection = firstCompletion.direction();
|
|
double largestTransmissibilityValue = firstCompletion.transmissibility();
|
|
|
|
RiaWeightedMeanCalculator<double> diameterCalculator;
|
|
RiaWeightedMeanCalculator<double> skinFactorCalculator;
|
|
|
|
for ( const RigCompletionData& completion : completions )
|
|
{
|
|
double transmissibility = completion.transmissibility();
|
|
|
|
diameterCalculator.addValueAndWeight( completion.diameter(), transmissibility );
|
|
skinFactorCalculator.addValueAndWeight( completion.skinFactor(), transmissibility );
|
|
|
|
if ( transmissibility > largestTransmissibilityValue )
|
|
{
|
|
largestTransmissibilityValue = transmissibility;
|
|
cellDirection = completion.direction();
|
|
}
|
|
}
|
|
|
|
double combinedDiameter = diameterCalculator.weightedMean();
|
|
double combinedSkinFactor = skinFactorCalculator.weightedMean();
|
|
|
|
double combinedTrans = 0.0;
|
|
double combinedKh = 0.0;
|
|
double combinedDFactor = 0.0;
|
|
|
|
{
|
|
RiaWeightedMeanCalculator<double> dFactorCalculator;
|
|
|
|
for ( const RigCompletionData& completion : completions )
|
|
{
|
|
resultCompletion.m_metadata.reserve( resultCompletion.m_metadata.size() + completion.m_metadata.size() );
|
|
resultCompletion.m_metadata.insert( resultCompletion.m_metadata.end(), completion.m_metadata.begin(), completion.m_metadata.end() );
|
|
|
|
if ( completion.wellName() != firstCompletion.wellName() )
|
|
{
|
|
QString errorMessage =
|
|
QString( "Cannot combine completions of different types in same cell %1" ).arg( cellIndexIJK.oneBasedLocalCellIndexString() );
|
|
RiaLogging::error( errorMessage );
|
|
resultCompletion.addMetadata( "ERROR", errorMessage );
|
|
return resultCompletion; // Returning empty completion, should not be exported
|
|
}
|
|
|
|
if ( completion.transmissibility() == HUGE_VAL )
|
|
{
|
|
QString errorMessage =
|
|
QString( "Transmissibility calculation has failed for cell %1" ).arg( cellIndexIJK.oneBasedLocalCellIndexString() );
|
|
RiaLogging::error( errorMessage );
|
|
resultCompletion.addMetadata( "ERROR", errorMessage );
|
|
return resultCompletion; // Returning empty completion, should not be exported
|
|
}
|
|
|
|
combinedTrans = combinedTrans + completion.transmissibility();
|
|
combinedKh = combinedKh + completion.kh();
|
|
|
|
dFactorCalculator.addValueAndWeight( completion.dFactor(), completion.transmissibility() );
|
|
}
|
|
|
|
// Arithmetic MEAN dFactor weighted by Tj/SumTj from the completions
|
|
// Note : Divide by n is intentional, based on input from @hhgs in mail dated 18.01.2020
|
|
combinedDFactor = dFactorCalculator.weightedMean() / completions.size();
|
|
}
|
|
|
|
if ( settings.compdatExport == RicExportCompletionDataSettingsUi::CompdatExport::TRANSMISSIBILITIES )
|
|
{
|
|
resultCompletion.setCombinedValuesExplicitTrans( combinedTrans,
|
|
combinedKh,
|
|
combinedDFactor,
|
|
combinedSkinFactor,
|
|
combinedDiameter,
|
|
cellDirection,
|
|
completionType );
|
|
}
|
|
else if ( settings.compdatExport == RicExportCompletionDataSettingsUi::CompdatExport::WPIMULT_AND_DEFAULT_CONNECTION_FACTORS )
|
|
{
|
|
// calculate trans for main bore - but as Eclipse will do it!
|
|
double transmissibilityEclipseCalculation =
|
|
RicWellPathExportCompletionDataFeatureImpl::calculateTransmissibilityAsEclipseDoes( settings.caseToApply(),
|
|
combinedSkinFactor,
|
|
combinedDiameter / 2,
|
|
cellIndexIJK.globalCellIndex(),
|
|
cellDirection );
|
|
|
|
double defaultKh = RigCompletionData::defaultValue();
|
|
double wpimult = combinedTrans / transmissibilityEclipseCalculation;
|
|
resultCompletion.setCombinedValuesImplicitTransWPImult( wpimult,
|
|
defaultKh,
|
|
combinedDFactor,
|
|
combinedSkinFactor,
|
|
combinedDiameter,
|
|
cellDirection,
|
|
completionType );
|
|
}
|
|
|
|
return resultCompletion;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RigCompletionData>
|
|
RicWellPathExportCompletionDataFeatureImpl::mainGridCompletions( const std::vector<RigCompletionData>& allCompletions )
|
|
{
|
|
std::vector<RigCompletionData> completions;
|
|
|
|
for ( const auto& completion : allCompletions )
|
|
{
|
|
QString gridName = completion.completionDataGridCell().lgrName();
|
|
if ( gridName.isEmpty() )
|
|
{
|
|
completions.push_back( completion );
|
|
}
|
|
}
|
|
return completions;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::map<QString, std::vector<RigCompletionData>>
|
|
RicWellPathExportCompletionDataFeatureImpl::subGridsCompletions( const std::vector<RigCompletionData>& allCompletions )
|
|
{
|
|
std::map<QString, std::vector<RigCompletionData>> completions;
|
|
|
|
for ( const auto& completion : allCompletions )
|
|
{
|
|
QString gridName = completion.completionDataGridCell().lgrName();
|
|
if ( !gridName.isEmpty() )
|
|
{
|
|
auto it = completions.find( gridName );
|
|
if ( it == completions.end() )
|
|
{
|
|
completions.insert( std::pair<QString, std::vector<RigCompletionData>>( gridName, { completion } ) );
|
|
}
|
|
else
|
|
{
|
|
it->second.push_back( completion );
|
|
}
|
|
}
|
|
}
|
|
return completions;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportCompletionDataFeatureImpl::exportWellPathFractureReport(
|
|
RimEclipseCase* sourceCase,
|
|
QFilePtr exportFile,
|
|
const std::vector<RicWellPathFractureReportItem>& wellPathFractureReportItems )
|
|
{
|
|
QTextStream stream( exportFile.get() );
|
|
|
|
if ( !wellPathFractureReportItems.empty() )
|
|
{
|
|
std::vector<RicWellPathFractureReportItem> sortedReportItems;
|
|
{
|
|
std::set<RicWellPathFractureReportItem> fractureReportItemsSet;
|
|
|
|
for ( const auto& reportItem : wellPathFractureReportItems )
|
|
{
|
|
fractureReportItemsSet.insert( reportItem );
|
|
}
|
|
|
|
for ( const auto& reportItem : fractureReportItemsSet )
|
|
{
|
|
sortedReportItems.emplace_back( reportItem );
|
|
}
|
|
}
|
|
|
|
std::vector<RimWellPath*> wellPathsToReport;
|
|
{
|
|
std::set<RimWellPath*> wellPathsSet;
|
|
|
|
auto allWellPaths = RicWellPathFractureTextReportFeatureImpl::wellPathsWithActiveFractures();
|
|
for ( const auto& wellPath : allWellPaths )
|
|
{
|
|
for ( const auto& reportItem : sortedReportItems )
|
|
{
|
|
if ( reportItem.wellPathNameForExport() == wellPath->completionSettings()->wellNameForExport() )
|
|
{
|
|
wellPathsSet.insert( wellPath );
|
|
}
|
|
}
|
|
}
|
|
|
|
std::copy( wellPathsSet.begin(), wellPathsSet.end(), std::back_inserter( wellPathsToReport ) );
|
|
}
|
|
|
|
RicWellPathFractureTextReportFeatureImpl reportGenerator;
|
|
QString summaryText = reportGenerator.wellPathFractureReport( sourceCase, wellPathsToReport, sortedReportItems );
|
|
|
|
stream << summaryText;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportCompletionDataFeatureImpl::exportWelspecsToFile( RimEclipseCase* gridCase,
|
|
QFilePtr exportFile,
|
|
const std::vector<RigCompletionData>& completions,
|
|
bool exportDataSourceAsComment )
|
|
{
|
|
QTextStream stream( exportFile.get() );
|
|
|
|
RifTextDataTableFormatter formatter( stream );
|
|
formatter.setColumnSpacing( 2 );
|
|
formatter.setOptionalComment( exportDataSourceAsComment );
|
|
|
|
std::vector<RifTextDataTableColumn> header = {
|
|
RifTextDataTableColumn( "WELL", "NAME" ), // well
|
|
RifTextDataTableColumn( "GROUP", "NAME" ), // group
|
|
RifTextDataTableColumn( "", "I" ), // I
|
|
RifTextDataTableColumn( "", "J" ), // J
|
|
RifTextDataTableColumn( "BHP", "DEPTH" ), // RefDepth
|
|
RifTextDataTableColumn( "PHASE", "FLUID" ), // Type
|
|
RifTextDataTableColumn( "DRAIN", "AREA" ), // DrainRad
|
|
RifTextDataTableColumn( "INFLOW", "EQUANS" ), // GasInEq
|
|
RifTextDataTableColumn( "OPEN", "SHUT" ), // AutoShut
|
|
RifTextDataTableColumn( "CROSS", "FLOW" ), // XFlow
|
|
RifTextDataTableColumn( "PVT", "TABLE" ), // FluidPVT
|
|
RifTextDataTableColumn( "HYDS", "DENS" ), // HydrDens
|
|
RifTextDataTableColumn( "FIP", "REGN" ) // FluidInPla) };
|
|
};
|
|
|
|
formatter.header( header );
|
|
formatter.keyword( "WELSPECS" );
|
|
|
|
std::set<const RimWellPath*> wellPathSet;
|
|
|
|
// Build list of unique RimWellPath
|
|
for ( const auto& completion : completions )
|
|
{
|
|
const auto wellPath = RicWellPathExportCompletionsFileTools::findWellPathFromExportName( completion.wellName() );
|
|
if ( wellPath )
|
|
{
|
|
wellPathSet.insert( wellPath );
|
|
}
|
|
}
|
|
|
|
// Export
|
|
for ( const auto wellPath : wellPathSet )
|
|
{
|
|
auto completionSettings = wellPath->completionSettings();
|
|
auto ijIntersection = wellPathUpperGridIntersectionIJ( gridCase, wellPath );
|
|
|
|
formatter.add( completionSettings->wellNameForExport() )
|
|
.add( completionSettings->groupNameForExport() )
|
|
.addOneBasedCellIndex( ijIntersection.second.x() )
|
|
.addOneBasedCellIndex( ijIntersection.second.y() )
|
|
.add( completionSettings->referenceDepthForExport() )
|
|
.add( completionSettings->wellTypeNameForExport() )
|
|
.add( completionSettings->drainageRadiusForExport() )
|
|
.add( completionSettings->gasInflowEquationForExport() )
|
|
.add( completionSettings->automaticWellShutInForExport() )
|
|
.add( completionSettings->allowWellCrossFlowForExport() )
|
|
.add( completionSettings->wellBoreFluidPVTForExport() )
|
|
.add( completionSettings->hydrostaticDensityForExport() )
|
|
.add( completionSettings->fluidInPlaceRegionForExport() )
|
|
.rowCompleted();
|
|
}
|
|
|
|
formatter.tableCompleted();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportCompletionDataFeatureImpl::exportWelspeclToFile( RimEclipseCase* gridCase,
|
|
QFilePtr exportFile,
|
|
const std::map<QString, std::vector<RigCompletionData>>& completions )
|
|
{
|
|
QTextStream stream( exportFile.get() );
|
|
|
|
RifTextDataTableFormatter formatter( stream );
|
|
formatter.setColumnSpacing( 2 );
|
|
|
|
std::vector<RifTextDataTableColumn> header = {
|
|
RifTextDataTableColumn( "WELL", "NAME" ), // well
|
|
RifTextDataTableColumn( "GROUP", "NAME" ), // group
|
|
RifTextDataTableColumn( "", "LGR" ),
|
|
RifTextDataTableColumn( "", "I" ), // I
|
|
RifTextDataTableColumn( "", "J" ), // J
|
|
RifTextDataTableColumn( "BHP", "DEPTH" ), // RefDepth
|
|
RifTextDataTableColumn( "PHASE", "FLUID" ), // Type
|
|
RifTextDataTableColumn( "DRAIN", "AREA" ), // DrainRad
|
|
RifTextDataTableColumn( "INFLOW", "EQUANS" ), // GasInEq
|
|
RifTextDataTableColumn( "OPEN", "SHUT" ), // AutoShut
|
|
RifTextDataTableColumn( "CROSS", "FLOW" ), // XFlow
|
|
RifTextDataTableColumn( "PVT", "TABLE" ), // FluidPVT
|
|
RifTextDataTableColumn( "HYDS", "DENS" ), // HydrDens
|
|
RifTextDataTableColumn( "FIP", "REGN" ) // FluidInPla) };
|
|
};
|
|
|
|
formatter.header( header );
|
|
formatter.keyword( "WELSPECL" );
|
|
|
|
std::map<const RimWellPath*, std::set<QString>> wellPathToLgrNameMap;
|
|
|
|
for ( const auto& completionsForLgr : completions )
|
|
{
|
|
for ( const auto& completion : completionsForLgr.second )
|
|
{
|
|
const auto wellPath = RicWellPathExportCompletionsFileTools::findWellPathFromExportName( completion.wellName() );
|
|
wellPathToLgrNameMap[wellPath].insert( completionsForLgr.first );
|
|
}
|
|
}
|
|
|
|
for ( const auto& wellPathsForLgr : wellPathToLgrNameMap )
|
|
{
|
|
const RimWellPath* wellPath = wellPathsForLgr.first;
|
|
|
|
std::tuple<double, cvf::Vec2i, QString> itemWithLowestMD = std::make_tuple( std::numeric_limits<double>::max(), cvf::Vec2i(), "" );
|
|
|
|
// Find first LGR-intersection along the well path
|
|
|
|
for ( const auto& lgrName : wellPathsForLgr.second )
|
|
{
|
|
auto ijIntersection = wellPathUpperGridIntersectionIJ( gridCase, wellPath, lgrName );
|
|
if ( ijIntersection.first < std::get<0>( itemWithLowestMD ) )
|
|
{
|
|
itemWithLowestMD = std::make_tuple( ijIntersection.first, ijIntersection.second, lgrName );
|
|
}
|
|
}
|
|
|
|
{
|
|
double measuredDepth = 0.0;
|
|
cvf::Vec2i ijIntersection;
|
|
QString lgrName;
|
|
|
|
std::tie( measuredDepth, ijIntersection, lgrName ) = itemWithLowestMD;
|
|
|
|
auto completionSettings = wellPath->completionSettings();
|
|
|
|
formatter.add( completionSettings->wellNameForExport() )
|
|
.add( completionSettings->groupNameForExport() )
|
|
.add( lgrName )
|
|
.addOneBasedCellIndex( ijIntersection.x() )
|
|
.addOneBasedCellIndex( ijIntersection.y() )
|
|
.add( completionSettings->referenceDepthForExport() )
|
|
.add( completionSettings->wellTypeNameForExport() )
|
|
.add( completionSettings->drainageRadiusForExport() )
|
|
.add( completionSettings->gasInflowEquationForExport() )
|
|
.add( completionSettings->automaticWellShutInForExport() )
|
|
.add( completionSettings->allowWellCrossFlowForExport() )
|
|
.add( completionSettings->wellBoreFluidPVTForExport() )
|
|
.add( completionSettings->hydrostaticDensityForExport() )
|
|
.add( completionSettings->fluidInPlaceRegionForExport() )
|
|
.rowCompleted();
|
|
}
|
|
}
|
|
formatter.tableCompleted();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportCompletionDataFeatureImpl::sortAndExportCompletionsToFile(
|
|
RimEclipseCase* eclipseCase,
|
|
const QString& folderName,
|
|
const QString& fileName,
|
|
const std::vector<RigCompletionData>& completions,
|
|
const std::vector<RicWellPathFractureReportItem>& wellPathFractureReportItems,
|
|
RicExportCompletionDataSettingsUi::CompdatExportType exportType,
|
|
bool exportDataSourceAsComment,
|
|
bool exportWelspec )
|
|
{
|
|
// Sort completions based on grid they belong to
|
|
std::vector<RigCompletionData> completionsForMainGrid = mainGridCompletions( completions );
|
|
std::map<QString, std::vector<RigCompletionData>> completionsForSubGrids = subGridsCompletions( completions );
|
|
|
|
if ( !completionsForMainGrid.empty() )
|
|
{
|
|
QFileInfo fi( fileName );
|
|
std::shared_ptr<QFile> exportFile =
|
|
RicWellPathExportCompletionsFileTools::openFileForExport( folderName, fi.baseName(), fi.suffix(), exportDataSourceAsComment );
|
|
|
|
if ( exportFile )
|
|
{
|
|
std::map<QString, std::vector<RigCompletionData>> completionsForGrid;
|
|
completionsForGrid.insert( std::pair<QString, std::vector<RigCompletionData>>( "", completionsForMainGrid ) );
|
|
|
|
exportWellPathFractureReport( eclipseCase, exportFile, wellPathFractureReportItems );
|
|
if ( exportWelspec )
|
|
{
|
|
exportWelspecsToFile( eclipseCase, exportFile, completionsForMainGrid, exportDataSourceAsComment );
|
|
}
|
|
exportCompdatAndWpimultTables( eclipseCase, exportFile, completionsForGrid, exportType, exportDataSourceAsComment );
|
|
}
|
|
}
|
|
|
|
if ( !completionsForSubGrids.empty() )
|
|
{
|
|
QFileInfo fi( fileName );
|
|
|
|
QString lgrFileName = fi.baseName() + "_LGR";
|
|
std::shared_ptr<QFile> exportFile =
|
|
RicWellPathExportCompletionsFileTools::openFileForExport( folderName, lgrFileName, fi.suffix(), exportDataSourceAsComment );
|
|
|
|
if ( exportFile )
|
|
{
|
|
exportWellPathFractureReport( eclipseCase, exportFile, wellPathFractureReportItems );
|
|
if ( exportWelspec )
|
|
{
|
|
exportWelspeclToFile( eclipseCase, exportFile, completionsForSubGrids );
|
|
}
|
|
exportCompdatAndWpimultTables( eclipseCase, exportFile, completionsForSubGrids, exportType, exportDataSourceAsComment );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportCompletionDataFeatureImpl::exportCompdatAndWpimultTables(
|
|
RimEclipseCase* sourceCase,
|
|
QFilePtr exportFile,
|
|
const std::map<QString, std::vector<RigCompletionData>>& completionsPerGrid,
|
|
RicExportCompletionDataSettingsUi::CompdatExportType exportType,
|
|
bool exportDataSourceAsComment )
|
|
{
|
|
if ( completionsPerGrid.empty() ) return;
|
|
|
|
QTextStream stream( exportFile.get() );
|
|
|
|
RifTextDataTableFormatter formatter( stream );
|
|
formatter.setColumnSpacing( 3 );
|
|
formatter.setOptionalComment( exportDataSourceAsComment );
|
|
|
|
for ( const auto& gridCompletions : completionsPerGrid )
|
|
{
|
|
std::vector<RigCompletionData> completions = gridCompletions.second;
|
|
|
|
// Sort by well name / cell index
|
|
std::sort( completions.begin(), completions.end() );
|
|
|
|
// Print completion data
|
|
QString gridName = gridCompletions.first;
|
|
exportCompdatTableUsingFormatter( formatter, gridName, completions );
|
|
|
|
if ( exportType == RicExportCompletionDataSettingsUi::CompdatExport::WPIMULT_AND_DEFAULT_CONNECTION_FACTORS )
|
|
{
|
|
exportWpimultTableUsingFormatter( formatter, gridName, completions );
|
|
}
|
|
}
|
|
|
|
RiaLogging::info( QString( "Successfully exported completion data to %1" ).arg( exportFile->fileName() ) );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportCompletionDataFeatureImpl::exportCompdatTableUsingFormatter( RifTextDataTableFormatter& formatter,
|
|
const QString& gridName,
|
|
const std::vector<RigCompletionData>& completionData )
|
|
{
|
|
if ( gridName.isEmpty() )
|
|
{
|
|
std::vector<RifTextDataTableColumn> header =
|
|
{ RifTextDataTableColumn( "WELL", "NAME" ),
|
|
RifTextDataTableColumn( "", "I" ),
|
|
RifTextDataTableColumn( "", "J" ),
|
|
RifTextDataTableColumn( "", "K1" ),
|
|
RifTextDataTableColumn( "", "K2" ),
|
|
RifTextDataTableColumn( "OPEN", "SHUT" ),
|
|
RifTextDataTableColumn( "SAT", "TAB" ),
|
|
RifTextDataTableColumn( "CONN", "FACT", RifTextDataTableDoubleFormatting( RifTextDataTableDoubleFormat::RIF_SCIENTIFIC ) ),
|
|
RifTextDataTableColumn( "WELL", "DIA" ),
|
|
RifTextDataTableColumn( "KH", "FACT", RifTextDataTableDoubleFormatting( RifTextDataTableDoubleFormat::RIF_SCIENTIFIC ) ),
|
|
RifTextDataTableColumn( "SKIN", "FACT" ),
|
|
RifTextDataTableColumn( "D", "FACT", RifTextDataTableDoubleFormatting( RifTextDataTableDoubleFormat::RIF_SCIENTIFIC ) ),
|
|
RifTextDataTableColumn( "DIR", "PEN" ) };
|
|
|
|
formatter.header( header );
|
|
formatter.keyword( "COMPDAT" );
|
|
}
|
|
else
|
|
{
|
|
std::vector<RifTextDataTableColumn> header =
|
|
{ RifTextDataTableColumn( "WELL", "NAME" ),
|
|
RifTextDataTableColumn( "LGR", "NAME" ),
|
|
RifTextDataTableColumn( "", "I" ),
|
|
RifTextDataTableColumn( "", "J" ),
|
|
RifTextDataTableColumn( "", "K1" ),
|
|
RifTextDataTableColumn( "", "K2" ),
|
|
RifTextDataTableColumn( "OPEN", "SHUT" ),
|
|
RifTextDataTableColumn( "SAT", "TAB" ),
|
|
RifTextDataTableColumn( "CONN", "FACT", RifTextDataTableDoubleFormatting( RifTextDataTableDoubleFormat::RIF_SCIENTIFIC ) ),
|
|
RifTextDataTableColumn( "WELL", "DIA" ),
|
|
RifTextDataTableColumn( "KH", "FACT", RifTextDataTableDoubleFormatting( RifTextDataTableDoubleFormat::RIF_SCIENTIFIC ) ),
|
|
RifTextDataTableColumn( "SKIN", "FACT" ),
|
|
RifTextDataTableColumn( "D", "FACT", RifTextDataTableDoubleFormatting( RifTextDataTableDoubleFormat::RIF_SCIENTIFIC ) ),
|
|
RifTextDataTableColumn( "DIR", "PEN" ) };
|
|
|
|
formatter.header( header );
|
|
formatter.keyword( "COMPDATL" );
|
|
}
|
|
|
|
RigCompletionData::CompletionType currentCompletionType = RigCompletionData::CompletionType::CT_UNDEFINED;
|
|
|
|
for ( const RigCompletionData& data : completionData )
|
|
{
|
|
if ( currentCompletionType != data.completionType() )
|
|
{
|
|
// The completions are sorted by completion type, write out a heading when completion type changes
|
|
|
|
QString txt;
|
|
if ( data.completionType() == RigCompletionData::CompletionType::FISHBONES ) txt = "Fishbones";
|
|
if ( data.completionType() == RigCompletionData::CompletionType::FRACTURE ) txt = "Fracture";
|
|
if ( data.completionType() == RigCompletionData::CompletionType::PERFORATION ) txt = "Perforation";
|
|
|
|
formatter.addOptionalComment( "---- Completions for completion type " + txt + " ----" );
|
|
|
|
currentCompletionType = data.completionType();
|
|
}
|
|
|
|
for ( const RigCompletionMetaData& metadata : data.metadata() )
|
|
{
|
|
formatter.addOptionalComment( QString( "%1 : %2" ).arg( metadata.name ).arg( metadata.comment ) );
|
|
}
|
|
|
|
if ( data.transmissibility() == 0.0 || data.wpimult() == 0.0 )
|
|
{
|
|
// Don't export completions without transmissibility
|
|
continue;
|
|
}
|
|
|
|
formatter.add( data.wellName() );
|
|
|
|
if ( !gridName.isEmpty() )
|
|
{
|
|
formatter.add( gridName );
|
|
}
|
|
|
|
formatter.addOneBasedCellIndex( data.completionDataGridCell().localCellIndexI() )
|
|
.addOneBasedCellIndex( data.completionDataGridCell().localCellIndexJ() )
|
|
.addOneBasedCellIndex( data.completionDataGridCell().localCellIndexK() )
|
|
.addOneBasedCellIndex( data.completionDataGridCell().localCellIndexK() );
|
|
|
|
formatter.add( "OPEN" );
|
|
|
|
formatter.addValueOrDefaultMarker( data.saturation(), RigCompletionData::defaultValue() );
|
|
formatter.addValueOrDefaultMarker( data.transmissibility(), RigCompletionData::defaultValue() );
|
|
formatter.addValueOrDefaultMarker( data.diameter(), RigCompletionData::defaultValue() );
|
|
formatter.addValueOrDefaultMarker( data.kh(), RigCompletionData::defaultValue() );
|
|
formatter.addValueOrDefaultMarker( data.skinFactor(), RigCompletionData::defaultValue() );
|
|
if ( RigCompletionData::isDefaultValue( data.dFactor() ) )
|
|
formatter.add( "1*" );
|
|
else
|
|
formatter.add( -data.dFactor() );
|
|
|
|
switch ( data.direction() )
|
|
{
|
|
case RigCompletionData::CellDirection::DIR_I:
|
|
formatter.add( "'X'" );
|
|
break;
|
|
case RigCompletionData::CellDirection::DIR_J:
|
|
formatter.add( "'Y'" );
|
|
break;
|
|
case RigCompletionData::CellDirection::DIR_K:
|
|
default:
|
|
formatter.add( "'Z'" );
|
|
break;
|
|
}
|
|
|
|
formatter.rowCompleted();
|
|
}
|
|
formatter.tableCompleted();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportCompletionDataFeatureImpl::exportWpimultTableUsingFormatter( RifTextDataTableFormatter& formatter,
|
|
const QString& gridName,
|
|
const std::vector<RigCompletionData>& completionData )
|
|
{
|
|
std::vector<RifTextDataTableColumn> header;
|
|
|
|
if ( gridName.isEmpty() )
|
|
{
|
|
header = {
|
|
RifTextDataTableColumn( "Well" ),
|
|
RifTextDataTableColumn( "Mult" ),
|
|
RifTextDataTableColumn( "I" ),
|
|
RifTextDataTableColumn( "J" ),
|
|
RifTextDataTableColumn( "K" ),
|
|
};
|
|
formatter.keyword( "WPIMULT" );
|
|
}
|
|
else
|
|
{
|
|
header = {
|
|
RifTextDataTableColumn( "Well" ),
|
|
RifTextDataTableColumn( "LgrName" ),
|
|
RifTextDataTableColumn( "Mult" ),
|
|
RifTextDataTableColumn( "I" ),
|
|
RifTextDataTableColumn( "J" ),
|
|
RifTextDataTableColumn( "K" ),
|
|
};
|
|
formatter.keyword( "WPIMULTL" );
|
|
}
|
|
formatter.header( header );
|
|
|
|
for ( auto& completion : completionData )
|
|
{
|
|
if ( completion.wpimult() == 0.0 || RigCompletionData::isDefaultValue( completion.wpimult() ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
formatter.add( completion.wellName() );
|
|
|
|
if ( !gridName.isEmpty() )
|
|
{
|
|
formatter.add( gridName );
|
|
}
|
|
|
|
formatter.add( completion.wpimult() );
|
|
|
|
formatter.addOneBasedCellIndex( completion.completionDataGridCell().localCellIndexI() )
|
|
.addOneBasedCellIndex( completion.completionDataGridCell().localCellIndexJ() )
|
|
.addOneBasedCellIndex( completion.completionDataGridCell().localCellIndexK() );
|
|
formatter.rowCompleted();
|
|
}
|
|
|
|
formatter.tableCompleted();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RigCompletionData>
|
|
RicWellPathExportCompletionDataFeatureImpl::generatePerforationsCompdatValues( gsl::not_null<const RimWellPath*> wellPath,
|
|
const std::vector<const RimPerforationInterval*>& intervals,
|
|
const RicExportCompletionDataSettingsUi& settings )
|
|
{
|
|
RiaDefines::EclipseUnitSystem unitSystem = settings.caseToApply->eclipseCaseData()->unitsType();
|
|
|
|
std::vector<RigCompletionData> completionData;
|
|
|
|
const RigActiveCellInfo* activeCellInfo =
|
|
settings.caseToApply->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
|
|
|
|
auto wellPathGeometry = wellPath->wellPathGeometry();
|
|
if ( !wellPathGeometry ) return completionData;
|
|
auto timeSteps = settings.caseToApply->timeStepDates();
|
|
|
|
const RimNonDarcyPerforationParameters* nonDarcyParameters = wellPath->perforationIntervalCollection()->nonDarcyParameters();
|
|
|
|
if ( wellPath->perforationIntervalCollection()->isChecked() )
|
|
{
|
|
for ( const RimPerforationInterval* interval : intervals )
|
|
{
|
|
if ( !interval->isChecked() ) continue;
|
|
if ( (size_t)settings.timeStep < timeSteps.size() &&
|
|
!interval->isActiveOnDate( settings.caseToApply->timeStepDates()[settings.timeStep] ) )
|
|
continue;
|
|
|
|
std::pair<std::vector<cvf::Vec3d>, std::vector<double>> perforationPointsAndMD =
|
|
wellPathGeometry->clippedPointSubset( interval->startMD(), interval->endMD() );
|
|
|
|
std::vector<WellPathCellIntersectionInfo> intersectedCells =
|
|
RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath( settings.caseToApply->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, settings.caseToApply->mainGrid() ),
|
|
cell.startMD );
|
|
|
|
RigCompletionData::CellDirection direction =
|
|
calculateCellMainDirection( settings.caseToApply, cell.globCellIndex, cell.intersectionLengthsInCellCS );
|
|
|
|
double transmissibility = 0.0;
|
|
double kh = RigCompletionData::defaultValue();
|
|
double dFactor = RigCompletionData::defaultValue();
|
|
|
|
{
|
|
auto transmissibilityData = calculateTransmissibilityData( settings.caseToApply,
|
|
wellPath,
|
|
cell.intersectionLengthsInCellCS,
|
|
interval->skinFactor(),
|
|
interval->diameter( unitSystem ) / 2,
|
|
cell.globCellIndex,
|
|
settings.useLateralNTG );
|
|
|
|
transmissibility = transmissibilityData.connectionFactor();
|
|
kh = transmissibilityData.kh();
|
|
|
|
if ( nonDarcyParameters->nonDarcyFlowType() == RimNonDarcyPerforationParameters::NON_DARCY_COMPUTED )
|
|
{
|
|
const double effectiveH = transmissibilityData.effectiveH();
|
|
|
|
const double effectivePermeability =
|
|
nonDarcyParameters->gridPermeabilityScalingFactor() * transmissibilityData.effectiveK();
|
|
|
|
dFactor = calculateDFactor( settings.caseToApply,
|
|
effectiveH,
|
|
cell.globCellIndex,
|
|
wellPath->perforationIntervalCollection()->nonDarcyParameters(),
|
|
effectivePermeability );
|
|
}
|
|
}
|
|
|
|
completion.setTransAndWPImultBackgroundDataFromPerforation( transmissibility,
|
|
interval->skinFactor(),
|
|
interval->diameter( unitSystem ),
|
|
dFactor,
|
|
kh,
|
|
direction );
|
|
completion.addMetadata( "Perforation Completion",
|
|
QString( "MD In: %1 - MD Out: %2" ).arg( cell.startMD ).arg( cell.endMD ) +
|
|
QString( " Transmissibility: " ) + QString::number( transmissibility ) );
|
|
completion.setSourcePdmObject( interval );
|
|
completionData.push_back( completion );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( nonDarcyParameters->nonDarcyFlowType() == RimNonDarcyPerforationParameters::NON_DARCY_USER_DEFINED )
|
|
{
|
|
// Compute D-factor for completion as
|
|
// D_cell = D_well * Sum_Tran_cells / Tran_cell
|
|
//
|
|
// See https://github.com/OPM/ResInsight/issues/7450
|
|
|
|
double accumulatedTransmissibility = 0.0;
|
|
for ( const auto& completion : completionData )
|
|
{
|
|
accumulatedTransmissibility += completion.transmissibility();
|
|
}
|
|
|
|
double userDefinedDFactor = nonDarcyParameters->userDefinedDFactor();
|
|
for ( auto& completion : completionData )
|
|
{
|
|
double dFactorForCompletion = userDefinedDFactor * accumulatedTransmissibility / completion.transmissibility();
|
|
completion.setDFactor( dFactorForCompletion );
|
|
}
|
|
}
|
|
|
|
return completionData;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportCompletionDataFeatureImpl::appendCompletionData( std::map<size_t, std::vector<RigCompletionData>>* completionData,
|
|
const std::vector<RigCompletionData>& completionsToAppend )
|
|
{
|
|
for ( const auto& completion : completionsToAppend )
|
|
{
|
|
auto it = completionData->find( completion.completionDataGridCell().globalCellIndex() );
|
|
if ( it != completionData->end() )
|
|
{
|
|
it->second.push_back( completion );
|
|
}
|
|
else
|
|
{
|
|
completionData->insert( std::pair<size_t, std::vector<RigCompletionData>>( completion.completionDataGridCell().globalCellIndex(),
|
|
std::vector<RigCompletionData>{ completion } ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RigCompletionData::CellDirection RicWellPathExportCompletionDataFeatureImpl::calculateCellMainDirection( RimEclipseCase* eclipseCase,
|
|
size_t globalCellIndex,
|
|
const cvf::Vec3d& lengthsInCell )
|
|
{
|
|
RigEclipseCaseData* eclipseCaseData = eclipseCase->eclipseCaseData();
|
|
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DX" ) );
|
|
cvf::ref<RigResultAccessor> dxAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "DX" ) );
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DY" ) );
|
|
cvf::ref<RigResultAccessor> dyAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "DY" ) );
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DZ" ) );
|
|
cvf::ref<RigResultAccessor> dzAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "DZ" ) );
|
|
|
|
double xLengthFraction = fabs( lengthsInCell.x() / dxAccessObject->cellScalarGlobIdx( globalCellIndex ) );
|
|
double yLengthFraction = fabs( lengthsInCell.y() / dyAccessObject->cellScalarGlobIdx( globalCellIndex ) );
|
|
double zLengthFraction = fabs( lengthsInCell.z() / dzAccessObject->cellScalarGlobIdx( globalCellIndex ) );
|
|
|
|
if ( xLengthFraction > yLengthFraction && xLengthFraction > zLengthFraction )
|
|
{
|
|
return RigCompletionData::CellDirection::DIR_I;
|
|
}
|
|
else if ( yLengthFraction > xLengthFraction && yLengthFraction > zLengthFraction )
|
|
{
|
|
return RigCompletionData::CellDirection::DIR_J;
|
|
}
|
|
else
|
|
{
|
|
return RigCompletionData::CellDirection::DIR_K;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
TransmissibilityData
|
|
RicWellPathExportCompletionDataFeatureImpl::calculateTransmissibilityData( RimEclipseCase* eclipseCase,
|
|
const RimWellPath* wellPath,
|
|
const cvf::Vec3d& internalCellLengths,
|
|
double skinFactor,
|
|
double wellRadius,
|
|
size_t globalCellIndex,
|
|
bool useLateralNTG,
|
|
size_t volumeScaleConstant,
|
|
RigCompletionData::CellDirection directionForVolumeScaling )
|
|
{
|
|
RigEclipseCaseData* eclipseCaseData = eclipseCase->eclipseCaseData();
|
|
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DX" ) );
|
|
cvf::ref<RigResultAccessor> dxAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "DX" ) );
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DY" ) );
|
|
cvf::ref<RigResultAccessor> dyAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "DY" ) );
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DZ" ) );
|
|
cvf::ref<RigResultAccessor> dzAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "DZ" ) );
|
|
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMX" ) );
|
|
cvf::ref<RigResultAccessor> permxAccessObject =
|
|
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "PERMX" ) );
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMY" ) );
|
|
cvf::ref<RigResultAccessor> permyAccessObject =
|
|
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "PERMY" ) );
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMZ" ) );
|
|
cvf::ref<RigResultAccessor> permzAccessObject =
|
|
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "PERMZ" ) );
|
|
|
|
if ( dxAccessObject.isNull() || dyAccessObject.isNull() || dzAccessObject.isNull() || permxAccessObject.isNull() ||
|
|
permyAccessObject.isNull() || permzAccessObject.isNull() )
|
|
{
|
|
return TransmissibilityData();
|
|
}
|
|
|
|
double ntg = 1.0;
|
|
{
|
|
// Trigger loading from file
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "NTG" ) );
|
|
|
|
cvf::ref<RigResultAccessor> ntgAccessObject =
|
|
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "NTG" ) );
|
|
|
|
if ( ntgAccessObject.notNull() )
|
|
{
|
|
ntg = ntgAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
}
|
|
}
|
|
double latNtg = useLateralNTG ? ntg : 1.0;
|
|
|
|
double dx = dxAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
double dy = dyAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
double dz = dzAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
double permx = permxAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
double permy = permyAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
double permz = permzAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
|
|
const double totalKh = RigTransmissibilityEquations::totalKh( permx, permy, permz, internalCellLengths, latNtg, ntg );
|
|
|
|
const double effectiveK = RigTransmissibilityEquations::effectiveK( permx, permy, permz, internalCellLengths, latNtg, ntg );
|
|
const double effectiveH = RigTransmissibilityEquations::effectiveH( internalCellLengths, latNtg, ntg );
|
|
|
|
double darcy = RiaEclipseUnitTools::darcysConstant( wellPath->unitSystem() );
|
|
|
|
if ( volumeScaleConstant != 1 )
|
|
{
|
|
if ( directionForVolumeScaling == RigCompletionData::CellDirection::DIR_I ) dx = dx / volumeScaleConstant;
|
|
if ( directionForVolumeScaling == RigCompletionData::CellDirection::DIR_J ) dy = dy / volumeScaleConstant;
|
|
if ( directionForVolumeScaling == RigCompletionData::CellDirection::DIR_K ) dz = dz / volumeScaleConstant;
|
|
}
|
|
|
|
const double transx = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( internalCellLengths.x() * latNtg,
|
|
permy,
|
|
permz,
|
|
dy,
|
|
dz,
|
|
wellRadius,
|
|
skinFactor,
|
|
darcy );
|
|
const double transy = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( internalCellLengths.y() * latNtg,
|
|
permx,
|
|
permz,
|
|
dx,
|
|
dz,
|
|
wellRadius,
|
|
skinFactor,
|
|
darcy );
|
|
const double transz = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( internalCellLengths.z() * ntg,
|
|
permy,
|
|
permx,
|
|
dy,
|
|
dx,
|
|
wellRadius,
|
|
skinFactor,
|
|
darcy );
|
|
|
|
const double totalConnectionFactor = RigTransmissibilityEquations::totalConnectionFactor( transx, transy, transz );
|
|
|
|
TransmissibilityData trData;
|
|
trData.setData( effectiveH, effectiveK, totalConnectionFactor, totalKh );
|
|
return trData;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
double RicWellPathExportCompletionDataFeatureImpl::calculateDFactor( RimEclipseCase* eclipseCase,
|
|
double effectiveH,
|
|
size_t globalCellIndex,
|
|
const RimNonDarcyPerforationParameters* nonDarcyParameters,
|
|
const double effectivePermeability )
|
|
{
|
|
using EQ = RigPerforationTransmissibilityEquations;
|
|
|
|
if ( !eclipseCase || !eclipseCase->eclipseCaseData() )
|
|
{
|
|
return std::numeric_limits<double>::infinity();
|
|
}
|
|
|
|
RigEclipseCaseData* eclipseCaseData = eclipseCase->eclipseCaseData();
|
|
|
|
double porosity = 0.0;
|
|
{
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PORO" ) );
|
|
cvf::ref<RigResultAccessor> poroAccessObject =
|
|
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "PORO" ) );
|
|
|
|
if ( poroAccessObject.notNull() )
|
|
{
|
|
porosity = poroAccessObject->cellScalar( globalCellIndex );
|
|
}
|
|
}
|
|
|
|
const double betaFactor = EQ::betaFactor( nonDarcyParameters->inertialCoefficientBeta0(),
|
|
effectivePermeability,
|
|
nonDarcyParameters->permeabilityScalingFactor(),
|
|
porosity,
|
|
nonDarcyParameters->porosityScalingFactor() );
|
|
|
|
const double alpha = RiaDefines::nonDarcyFlowAlpha( eclipseCaseData->unitsType() );
|
|
|
|
return EQ::dFactor( alpha,
|
|
betaFactor,
|
|
effectivePermeability,
|
|
effectiveH,
|
|
nonDarcyParameters->wellRadius(),
|
|
nonDarcyParameters->relativeGasDensity(),
|
|
nonDarcyParameters->gasViscosity() );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
double RicWellPathExportCompletionDataFeatureImpl::calculateTransmissibilityAsEclipseDoes( RimEclipseCase* eclipseCase,
|
|
double skinFactor,
|
|
double wellRadius,
|
|
size_t globalCellIndex,
|
|
RigCompletionData::CellDirection direction )
|
|
{
|
|
RigEclipseCaseData* eclipseCaseData = eclipseCase->eclipseCaseData();
|
|
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DX" ) );
|
|
cvf::ref<RigResultAccessor> dxAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "DX" ) );
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DY" ) );
|
|
cvf::ref<RigResultAccessor> dyAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "DY" ) );
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "DZ" ) );
|
|
cvf::ref<RigResultAccessor> dzAccessObject = RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "DZ" ) );
|
|
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMX" ) );
|
|
cvf::ref<RigResultAccessor> permxAccessObject =
|
|
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "PERMX" ) );
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMY" ) );
|
|
cvf::ref<RigResultAccessor> permyAccessObject =
|
|
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "PERMY" ) );
|
|
eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "PERMZ" ) );
|
|
cvf::ref<RigResultAccessor> permzAccessObject =
|
|
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "PERMZ" ) );
|
|
|
|
double ntg = 1.0;
|
|
if ( eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( RigEclipseResultAddress( "NTG" ) ) )
|
|
{
|
|
cvf::ref<RigResultAccessor> ntgAccessObject =
|
|
RigResultAccessorFactory::createFromResultAddress( eclipseCaseData,
|
|
0,
|
|
RiaDefines::PorosityModelType::MATRIX_MODEL,
|
|
0,
|
|
RigEclipseResultAddress( "NTG" ) );
|
|
ntg = ntgAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
}
|
|
|
|
double dx = dxAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
double dy = dyAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
double dz = dzAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
double permx = permxAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
double permy = permyAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
double permz = permzAccessObject->cellScalarGlobIdx( globalCellIndex );
|
|
|
|
RiaDefines::EclipseUnitSystem units = eclipseCaseData->unitsType();
|
|
double darcy = RiaEclipseUnitTools::darcysConstant( units );
|
|
|
|
double trans = cvf::UNDEFINED_DOUBLE;
|
|
if ( direction == RigCompletionData::CellDirection::DIR_I )
|
|
{
|
|
trans = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( dx, permy, permz, dy, dz, wellRadius, skinFactor, darcy );
|
|
}
|
|
else if ( direction == RigCompletionData::CellDirection::DIR_J )
|
|
{
|
|
trans = RigTransmissibilityEquations::wellBoreTransmissibilityComponent( dy, permx, permz, dx, dz, wellRadius, skinFactor, darcy );
|
|
}
|
|
else if ( direction == RigCompletionData::CellDirection::DIR_K )
|
|
{
|
|
trans =
|
|
RigTransmissibilityEquations::wellBoreTransmissibilityComponent( dz * ntg, permy, permx, dy, dx, wellRadius, skinFactor, darcy );
|
|
}
|
|
|
|
return trans;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::pair<double, cvf::Vec2i>
|
|
RicWellPathExportCompletionDataFeatureImpl::wellPathUpperGridIntersectionIJ( gsl::not_null<const RimEclipseCase*> gridCase,
|
|
gsl::not_null<const RimWellPath*> wellPath,
|
|
const QString& gridName )
|
|
{
|
|
const RigEclipseCaseData* caseData = gridCase->eclipseCaseData();
|
|
const RigMainGrid* mainGrid = caseData->mainGrid();
|
|
const RigActiveCellInfo* activeCellInfo = caseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
|
|
|
|
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( caseData, wellPath->name(), coords, mds );
|
|
|
|
int gridId = 0;
|
|
|
|
if ( !gridName.isEmpty() )
|
|
{
|
|
const auto grid = caseData->grid( gridName );
|
|
if ( grid ) gridId = grid->gridId();
|
|
}
|
|
|
|
for ( const WellPathCellIntersectionInfo& intersection : intersections )
|
|
{
|
|
size_t gridLocalCellIndex = 0;
|
|
const RigGridBase* grid = mainGrid->gridAndGridLocalIdxFromGlobalCellIdx( intersection.globCellIndex, &gridLocalCellIndex );
|
|
|
|
if ( grid->gridId() == gridId && activeCellInfo->isActive( intersection.globCellIndex ) )
|
|
{
|
|
size_t i, j, k;
|
|
if ( grid->ijkFromCellIndex( gridLocalCellIndex, &i, &j, &k ) )
|
|
{
|
|
return std::make_pair( intersection.startMD, cvf::Vec2i( (int)i, (int)j ) );
|
|
}
|
|
}
|
|
}
|
|
return std::make_pair( cvf::UNDEFINED_DOUBLE, cvf::Vec2i() );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimWellPath* RicWellPathExportCompletionDataFeatureImpl::topLevelWellPath( const RigCompletionData& completion )
|
|
{
|
|
if ( completion.sourcePdmObject() )
|
|
{
|
|
auto parentWellPath = completion.sourcePdmObject()->firstAncestorOrThisOfType<RimWellPath>();
|
|
if ( parentWellPath )
|
|
{
|
|
return parentWellPath->topLevelWellPath();
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RicWellPathExportCompletionDataFeatureImpl::exportCarfinForTemporaryLgrs( const RimEclipseCase* sourceCase, const QString& folder )
|
|
{
|
|
if ( !sourceCase || !sourceCase->mainGrid() ) return;
|
|
|
|
const auto mainGrid = sourceCase->mainGrid();
|
|
const auto& lgrInfosForWells = RicExportLgrFeature::createLgrInfoListForTemporaryLgrs( mainGrid );
|
|
|
|
for ( const auto& lgrInfoForWell : lgrInfosForWells )
|
|
{
|
|
RicExportLgrFeature::exportLgrs( folder, lgrInfoForWell.first, lgrInfoForWell.second );
|
|
}
|
|
}
|