///////////////////////////////////////////////////////////////////////////////// // // 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 // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RicWellPathExportCompletionDataFeatureImpl.h" #include "RiaLogging.h" #include "RiaPreferencesSystem.h" #include "RiaQStringFormatter.h" #include "RiuMessageDialog.h" #include "ExportCommands/RicExportLgrFeature.h" #include "RicCompletionDataExportTools.h" #include "RicExportCompletionDataSettingsUi.h" #include "RicExportFractureCompletionsImpl.h" #include "RicFishbonesTransmissibilityCalculationFeatureImp.h" #include "RicScheduleDataGenerator.h" #include "RicTransmissibilityCalculator.h" #include "RicWellPathExportCompletionsFileTools.h" #include "RicWellPathExportMswCompletionsImpl.h" #include "RicWellPathFractureReportItem.h" #include "RicWellPathFractureTextReportFeatureImpl.h" #include "RifTextDataTableFormatter.h" #include "RifThermalToStimPlanFractureXmlOutput.h" #include "RigActiveCellInfo.h" #include "RigEclipseCaseData.h" #include "RigMainGrid.h" #include "Well/RigWellLogExtractor.h" #include "Well/RigWellPath.h" #include "Well/RigWellPathIntersectionTools.h" #include "RimCellFilter.h" #include "RimEclipseCase.h" #include "RimFishbonesCollection.h" #include "RimPerforationCollection.h" #include "RimThermalFractureTemplate.h" #include "RimWellPath.h" #include "RimWellPathCompletions.h" #include "RimWellPathFracture.h" #include "RimWellPathFractureCollection.h" #include "Riu3DMainWindowTools.h" #include "cafProgressInfo.h" #include #include #include #include #include #include //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RicWellPathExportCompletionDataFeatureImpl::exportCompletions( const std::vector& allWellPaths, const RicExportCompletionDataSettingsUi& exportSettings ) { // Laterals are exported together with their top level well path const auto wellPaths = RicWellPathExportCompletionsFileTools::uniqueTopLevelWellPaths( allWellPaths ); 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 ); RiuMessageDialog::showError( Riu3DMainWindowTools::mainWindowWidget(), "Export", txt ); return; } } exportCarfinForTemporaryLgrs( exportSettings.caseToApply(), exportSettings.folder ); { std::vector fractureDataReportItems; std::unique_ptr 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.toStdString() ); return; } QString fractureTransmisibillityExportInformationPath = QDir( exportSettings.folder ).absoluteFilePath( "FractureTransmissibilityExportInformation" ); fractureTransmissibilityExportInformationFile.setFileName( fractureTransmisibillityExportInformationPath ); if ( !fractureTransmissibilityExportInformationFile.open( QIODevice::WriteOnly | QIODevice::Text ) ) { RiaLogging::error( std::format( "Export Completions Data: Could not open the file: {}", fractureTransmisibillityExportInformationPath ) ); } else { fractureTransmissibilityExportInformationStream = std::make_unique( &fractureTransmissibilityExportInformationFile ); } // Extra debugging for thermal fractures if ( exportSettings.includeFractures() ) { for ( RimWellPath* wellPath : wellPaths ) { std::vector allWellPathLaterals = wellPath->allWellPathLaterals(); for ( auto wellPathLateral : allWellPathLaterals ) { for ( auto& frac : wellPathLateral->fractureCollection()->activeFractures() ) { if ( RimThermalFractureTemplate* fractureTemplate = dynamic_cast( frac->fractureTemplate() ) ) { QString name = frac->name(); QString fractureXmlPath = QDir( exportSettings.folder ).absoluteFilePath( name + ".xml" ); RifThermalToStimPlanFractureXmlOutput::writeToFile( fractureTemplate, fractureXmlPath ); } } } } } } std::vector completions; { caf::ProgressInfo progress( wellPaths.size(), "Extracting Completion Data For Well Paths" ); for ( RimWellPath* wellPath : wellPaths ) { std::vector 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.toStdString() ); } std::map> completionsPerEclipseCellAllCompletionTypes; 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 ) { auto exportDate = exportDateForTimeStep( exportSettings.caseToApply, exportSettings.timeStep ); std::vector perforationCompletionData = RicCompletionDataExportTools::generatePerforationsCompdatValues( wellPathLateral, wellPathLateral ->perforationIntervalCollection() ->perforations(), exportSettings, exportDate ); RicCompletionDataExportTools::appendCompletionData( &completionsPerEclipseCellAllCompletionTypes, perforationCompletionData ); } if ( exportSettings.includeFishbones ) { // Make sure the start and end location is computed if needed wellPathLateral->fishbonesCollection()->computeStartAndEndLocation(); std::vector fishbonesCompletionData = RicFishbonesTransmissibilityCalculationFeatureImp::generateFishboneCompdatValuesUsingAdjustedCellVolume( wellPathLateral, exportSettings ); RicCompletionDataExportTools::appendCompletionData( &completionsPerEclipseCellAllCompletionTypes, fishbonesCompletionData ); } if ( exportSettings.includeFractures() ) { // If no report is wanted, set reportItems = nullptr std::vector* reportItems = &fractureDataReportItems; std::vector fractureCompletionData = RicExportFractureCompletionsImpl:: generateCompdatValuesForWellPath( wellPathLateral, exportSettings.caseToApply(), reportItems, fractureTransmissibilityExportInformationStream.get(), RicExportFractureCompletionsImpl:: PressureDepletionParameters( exportSettings.performTransScaling(), exportSettings.transScalingTimeStep(), exportSettings.transScalingWBHPSource(), exportSettings.transScalingWBHP() ) ); RicCompletionDataExportTools::appendCompletionData( &completionsPerEclipseCellAllCompletionTypes, fractureCompletionData ); } } for ( auto& data : completionsPerEclipseCellAllCompletionTypes ) { completions.push_back( RicCompletionDataExportTools::combineEclipseCellCompletions( data.second, exportSettings ) ); } progress.incrementProgress(); } } // make sure we use per-connection D-factors, must be done after combining completions for ( auto& completion : completions ) { completion.setPerConnectionDfactor(); } 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 completionsForWell; for ( const auto& completion : completions ) { if ( wellPath == RicCompletionDataExportTools::topLevelWellPath( completion ) ) { completionsForWell.push_back( completion ); } } if ( completionsForWell.empty() ) continue; std::vector reportItemsForWell; for ( const auto& fracItem : fractureDataReportItems ) { if ( fracItem.wellPathNameForExport() == wellPath->completionSettings()->wellNameForExport() ) { reportItemsForWell.push_back( fracItem ); } } QString fileName = QString( "%1_Completions_%2" ).arg( wellPath->name() ).arg( eclipseCaseName ); sortAndExportCompletionsToFile( exportSettings.caseToApply, exportSettings.folder, fileName, completionsForWell, reportItemsForWell, exportSettings.compdatExport, exportSettings.exportDataSourceAsComment(), exportSettings.exportWelspec() ); } } } if ( exportSettings.includeMsw ) { RicWellPathExportMswCompletionsImpl::exportWellSegmentsForAllCompletions( exportSettings, wellPaths ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RicWellPathExportCompletionDataFeatureImpl::computeStaticCompletionsForWellPath( RimWellPath* wellPath, RimEclipseCase* eclipseCase ) { std::vector completionsPerEclipseCell; if ( eclipseCase && eclipseCase->eclipseCaseData() ) { RicExportCompletionDataSettingsUi exportSettings; exportSettings.caseToApply = eclipseCase; exportSettings.timeStep = 0; exportSettings.includeFishbones = true; exportSettings.includePerforations = true; exportSettings.includeFractures = true; { std::vector completionData = RicFishbonesTransmissibilityCalculationFeatureImp::generateFishboneCompdatValuesUsingAdjustedCellVolume( wellPath, exportSettings ); std::copy( completionData.begin(), completionData.end(), std::back_inserter( completionsPerEclipseCell ) ); } { std::vector completionData = RicExportFractureCompletionsImpl::generateCompdatValuesForWellPath( wellPath, eclipseCase, nullptr, nullptr ); std::copy( completionData.begin(), completionData.end(), std::back_inserter( completionsPerEclipseCell ) ); } } return completionsPerEclipseCell; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RicWellPathExportCompletionDataFeatureImpl::computeDynamicCompletionsForWellPath( RimWellPath* wellPath, RimEclipseCase* eclipseCase, size_t timeStepIndex ) { CAF_ASSERT( eclipseCase ); std::vector completionsPerEclipseCell; if ( eclipseCase && eclipseCase->eclipseCaseData() ) { RicExportCompletionDataSettingsUi exportSettings; exportSettings.caseToApply = eclipseCase; exportSettings.timeStep = static_cast( timeStepIndex ); exportSettings.includeFishbones = true; exportSettings.includePerforations = true; exportSettings.includeFractures = true; auto exportDate = exportDateForTimeStep( eclipseCase, timeStepIndex ); completionsPerEclipseCell = RicCompletionDataExportTools::generatePerforationsCompdatValues( wellPath, wellPath->perforationIntervalCollection()->perforations(), exportSettings, exportDate ); } return completionsPerEclipseCell; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RicWellPathExportCompletionDataFeatureImpl::exportWellPathFractureReport( RimEclipseCase* sourceCase, QFilePtr exportFile, const std::vector& wellPathFractureReportItems ) { QTextStream stream( exportFile.get() ); if ( !wellPathFractureReportItems.empty() ) { std::vector sortedReportItems; { std::set fractureReportItemsSet; for ( const auto& reportItem : wellPathFractureReportItems ) { fractureReportItemsSet.insert( reportItem ); } for ( const auto& reportItem : fractureReportItemsSet ) { sortedReportItems.emplace_back( reportItem ); } } std::vector wellPathsToReport; { std::set 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& completions, bool exportDataSourceAsComment ) { QTextStream stream( exportFile.get() ); RifTextDataTableFormatter formatter( stream ); formatter.setColumnSpacing( 2 ); formatter.setOptionalComment( exportDataSourceAsComment ); std::vector 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 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() ) .add( ijIntersection.second.x() + 1 ) .add( ijIntersection.second.y() + 1 ) .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>& completions ) { QTextStream stream( exportFile.get() ); RifTextDataTableFormatter formatter( stream ); formatter.setColumnSpacing( 2 ); std::vector 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> wellPathToLgrNameMap; for ( const auto& completionsForLgr : completions ) { for ( const auto& completion : completionsForLgr.second ) { const auto wellPath = RicWellPathExportCompletionsFileTools::findWellPathFromExportName( completion.wellName() ); if ( !wellPath ) continue; wellPathToLgrNameMap[wellPath].insert( completionsForLgr.first ); } } // See similar logic in RiaGrpcWellPathService::GetCompletionData() // A well must be defined only once. Use the LGR the well path enters first. for ( const auto& [wellPath, gridNames] : wellPathToLgrNameMap ) { QString firstLgrName; double firstMeasuredDepth = std::numeric_limits::max(); cvf::Vec2i firstIJ; bool isFound = false; for ( const auto& lgrName : gridNames ) { const auto& [measuredDepth, ijIntersection] = wellPathUpperGridIntersectionIJ( gridCase, wellPath, lgrName ); const bool isValid = measuredDepth != cvf::UNDEFINED_DOUBLE; if ( !isFound || ( isValid && measuredDepth < firstMeasuredDepth ) ) { firstLgrName = lgrName; firstMeasuredDepth = isValid ? measuredDepth : std::numeric_limits::max(); firstIJ = ijIntersection; isFound = true; } } if ( !isFound ) continue; auto completionSettings = wellPath->completionSettings(); formatter.add( completionSettings->wellNameForExport() ) .add( completionSettings->groupNameForExport() ) .add( firstLgrName ) .add( firstIJ.x() + 1 ) .add( firstIJ.y() + 1 ) .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& completions, const std::vector& wellPathFractureReportItems, RicExportCompletionDataSettingsUi::CompdatExportType exportType, bool exportDataSourceAsComment, bool exportWelspec ) { // Sort completions based on grid they belong to std::vector completionsForMainGrid = RicCompletionDataExportTools::mainGridCompletions( completions ); std::map> completionsForSubGrids = RicCompletionDataExportTools::subGridsCompletions( completions ); if ( !completionsForMainGrid.empty() ) { QFileInfo fi( fileName ); std::shared_ptr exportFile = RicWellPathExportCompletionsFileTools::openFileForExport( folderName, fi.baseName(), fi.suffix(), exportDataSourceAsComment ); if ( exportFile ) { std::map> completionsForGrid; completionsForGrid.insert( std::pair>( "", 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 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>& completionsPerGrid, RicExportCompletionDataSettingsUi::CompdatExportType exportType, bool exportDataSourceAsComment ) { if ( completionsPerGrid.empty() ) return; QTextStream stream( exportFile.get() ); RifTextDataTableFormatter formatter( stream ); formatter.setColumnSpacing( 3 ); formatter.setOptionalComment( exportDataSourceAsComment ); // All LGRs share one set of tables (COMPDATL, WPIMULTL, COMPLMPL) where each row carries its own LGR name std::map> completionsPerTable; for ( const auto& [gridName, gridCompletions] : completionsPerGrid ) { auto& tableCompletions = completionsPerTable[gridName.isEmpty() ? QString() : QString( "LGR" )]; tableCompletions.insert( tableCompletions.end(), gridCompletions.begin(), gridCompletions.end() ); } for ( const auto& gridCompletions : completionsPerTable ) { std::vector completions = gridCompletions.second; // Sort by well name / cell index std::sort( completions.begin(), completions.end() ); // Print completion data QString gridName = gridCompletions.first; RicCompletionDataExportTools::exportCompdatTableUsingFormatter( formatter, gridName, completions ); if ( exportType == RicExportCompletionDataSettingsUi::CompdatExport::WPIMULT_AND_DEFAULT_CONNECTION_FACTORS ) { RicCompletionDataExportTools::exportWpimultTableUsingFormatter( formatter, gridName, completions ); } for ( auto& completion : completions ) { if ( completion.completionNumber().has_value() ) { RicCompletionDataExportTools::exportComplumpTableUsingFormatter( formatter, gridName, completions ); break; } } } RiaLogging::info( std::format( "Successfully exported completion data to {}", exportFile->fileName() ) ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::pair RicWellPathExportCompletionDataFeatureImpl::wellPathUpperGridIntersectionIJ( const RimEclipseCase* gridCase, 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(); if ( !wellPathGeometry ) return std::make_pair( cvf::UNDEFINED_DOUBLE, cvf::Vec2i() ); const std::vector& coords = wellPathGeometry->wellPathPoints(); const std::vector& mds = wellPathGeometry->measuredDepths(); if ( coords.empty() || mds.empty() ) return std::make_pair( cvf::UNDEFINED_DOUBLE, cvf::Vec2i() ); std::vector 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( ReservoirCellIndex( 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() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- 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 ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RicWellPathExportCompletionDataFeatureImpl::completionDataForWellPath( RimWellPath* wellPath, RimEclipseCase* eCase, const std::optional& exportDate ) { if ( wellPath == nullptr ) return {}; return completionDataForWellPaths( { wellPath }, eCase, exportDate ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RicWellPathExportCompletionDataFeatureImpl::completionDataForWellPaths( const std::vector& wellPaths, RimEclipseCase* eCase, const std::optional& exportDate ) { if ( eCase == nullptr ) return {}; // Ensure that the case is open. This will enable export without any open views. eCase->ensureReservoirCaseIsOpen(); if ( eCase->eclipseCaseData() == nullptr ) { RiaLogging::error( "Export Completions Data: No data available for Eclipse Case" ); return {}; } RicExportCompletionDataSettingsUi exportSettings; exportSettings.caseToApply = eCase; exportSettings.includeMsw = false; exportSettings.setExportDataSourceAsComment( true ); exportSettings.includePerforations = true; exportSettings.includeFishbones = true; exportSettings.includeFractures = true; std::vector completions; // Completions from all given well paths (and their laterals) are combined per cell, so that cells shared // by several well paths are exported as a single connection. std::vector allWellPathLaterals; for ( auto* wellPath : wellPaths ) { if ( !wellPath || wellPath->unitSystem() != eCase->eclipseCaseData()->unitsType() ) continue; for ( auto* lateral : wellPath->allWellPathLaterals() ) { if ( std::find( allWellPathLaterals.begin(), allWellPathLaterals.end(), lateral ) == allWellPathLaterals.end() ) { allWellPathLaterals.push_back( lateral ); } } } std::map> completionsPerEclipseCellAllCompletionTypes; 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 perforationCompletionData = RicCompletionDataExportTools::generatePerforationsCompdatValues( wellPathLateral, wellPathLateral->perforationIntervalCollection()->perforations(), exportSettings, exportDate ); RicCompletionDataExportTools::appendCompletionData( &completionsPerEclipseCellAllCompletionTypes, perforationCompletionData ); } if ( exportSettings.includeFishbones ) { // Make sure the start and end location is computed if needed wellPathLateral->fishbonesCollection()->computeStartAndEndLocation(); std::vector fishbonesCompletionData = RicFishbonesTransmissibilityCalculationFeatureImp::generateFishboneCompdatValuesUsingAdjustedCellVolume( wellPathLateral, exportSettings ); RicCompletionDataExportTools::appendCompletionData( &completionsPerEclipseCellAllCompletionTypes, fishbonesCompletionData ); } if ( exportSettings.includeFractures() ) { // If no report is wanted, set reportItems = nullptr std::vector* reportItems = nullptr; std::vector fractureCompletionData = RicExportFractureCompletionsImpl:: generateCompdatValuesForWellPath( wellPathLateral, exportSettings.caseToApply(), reportItems, nullptr, RicExportFractureCompletionsImpl:: PressureDepletionParameters( exportSettings.performTransScaling(), exportSettings.transScalingTimeStep(), exportSettings.transScalingWBHPSource(), exportSettings.transScalingWBHP() ) ); RicCompletionDataExportTools::appendCompletionData( &completionsPerEclipseCellAllCompletionTypes, fractureCompletionData ); } } for ( auto& data : completionsPerEclipseCellAllCompletionTypes ) { completions.push_back( RicCompletionDataExportTools::combineEclipseCellCompletions( data.second, exportSettings ) ); } // Order completions with each well's mainbore connections first, followed by each lateral's // (see https://github.com/OPM/ResInsight/issues/14963), instead of the plain well // name/completion type/measured depth ordering from RigCompletionData::operator<(), since each // lateral has its own independent MD scale. RicScheduleDataGenerator::sortCompletionsMainBoreFirst( wellPaths, completions ); // make sure we set per-connection D-factors, must be done after combining completions for ( auto& completion : completions ) { completion.setPerConnectionDfactor(); // we calculate per connection D-factors, which are negative per definition (ref. OPM Flow // manual) } return completions; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::optional RicWellPathExportCompletionDataFeatureImpl::exportDateForTimeStep( const RimEclipseCase* eclipseCase, size_t timeStepIndex ) { if ( !eclipseCase ) return std::nullopt; auto timeSteps = eclipseCase->timeStepDates(); if ( timeStepIndex < timeSteps.size() ) return timeSteps[timeStepIndex]; return std::nullopt; }