ResInsight/ApplicationLibCode/Commands/ExportCommands/RicExportLgrFeature.cpp
2023-09-11 17:39:38 +02:00

970 lines
40 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 "RicExportLgrFeature.h"
#include "RiaApplication.h"
#include "RiaLogging.h"
#include "CompletionExportCommands/RicWellPathExportCompletionDataFeature.h"
#include "RicExportLgrUi.h"
#include "RifTextDataTableFormatter.h"
#include "RigCaseCellResultsData.h"
#include "RigMainGrid.h"
#include "RigResultAccessor.h"
#include "RigResultAccessorFactory.h"
#include "RigVirtualPerforationTransmissibilities.h"
#include "RigWellLogExtractor.h"
#include "RigWellPath.h"
#include "RigWellPathIntersectionTools.h"
#include "RimDialogData.h"
#include "RimEclipseCase.h"
#include "RimEclipseView.h"
#include "RimFishbonesCollection.h"
#include "RimPerforationCollection.h"
#include "RimProject.h"
#include "RimWellPath.h"
#include "RimWellPathCollection.h"
#include "RimWellPathCompletions.h"
#include "RimWellPathFractureCollection.h"
#include "RiuPlotMainWindow.h"
#include "RimFishbones.h"
#include "RimPerforationInterval.h"
#include "RimWellPathFracture.h"
#include <QAction>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QStringList>
#include <QTextStream>
#include <cafPdmUiPropertyViewDialog.h>
#include <cafSelectionManager.h>
#include <cafSelectionManagerTools.h>
#include <cafUtils.h>
#include <cafVecIjk.h>
#include <algorithm>
#include <array>
#include <limits>
#include <set>
CAF_CMD_SOURCE_INIT( RicExportLgrFeature, "RicExportLgrFeature" );
//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
#define DOUBLE_INF std::numeric_limits<double>::infinity()
//--------------------------------------------------------------------------------------------------
/// Internal class
//--------------------------------------------------------------------------------------------------
class CellInfo
{
public:
CellInfo( size_t globCellIndex )
: globCellIndex( globCellIndex )
, startMd( DOUBLE_INF )
, endMd( DOUBLE_INF )
{
}
CellInfo( size_t globCellIndex, double startMd, double endMd )
: globCellIndex( globCellIndex )
, startMd( startMd )
, endMd( endMd )
{
}
size_t globCellIndex;
double startMd;
double endMd;
bool operator<( const CellInfo& other ) const { return startMd < other.startMd; }
};
//--------------------------------------------------------------------------------------------------
/// Internal class
//--------------------------------------------------------------------------------------------------
class LgrNameFactory
{
public:
LgrNameFactory();
QString newName( RigCompletionData::CompletionType completionType );
QString newName( const QString& baseName, int number );
private:
std::map<RigCompletionData::CompletionType, std::pair<QString, int>> m_counters;
};
//--------------------------------------------------------------------------------------------------
/// Internal class
//--------------------------------------------------------------------------------------------------
class IjkBoundingBox
{
const size_t MAX_SIZE_T = std::numeric_limits<size_t>::max();
enum Index
{
I,
J,
K
};
public:
IjkBoundingBox()
: m_min( { MAX_SIZE_T, MAX_SIZE_T, MAX_SIZE_T } )
, m_max( { MAX_SIZE_T, MAX_SIZE_T, MAX_SIZE_T } )
{
}
IjkBoundingBox( const IjkBoundingBox& other )
: m_min( other.m_min )
, m_max( other.m_max )
{
}
IjkBoundingBox( const caf::VecIjk& minCell, const caf::VecIjk& maxCell )
{
m_min[I] = minCell.i();
m_min[J] = minCell.j();
m_min[K] = minCell.k();
m_max[I] = maxCell.i();
m_max[J] = maxCell.j();
m_max[K] = maxCell.k();
}
IjkBoundingBox& operator=( const IjkBoundingBox& other )
{
m_min = other.m_min;
m_max = other.m_max;
return *this;
}
bool isValid() const
{
return m_min[I] != MAX_SIZE_T && m_min[J] != MAX_SIZE_T && m_min[K] != MAX_SIZE_T && m_max[I] != MAX_SIZE_T &&
m_max[J] != MAX_SIZE_T && m_max[K] != MAX_SIZE_T;
}
void addCell( size_t i, size_t j, size_t k )
{
if ( !isValid() )
{
m_min = m_max = { i, j, k };
}
else
{
if ( i < m_min[I] ) m_min[I] = i;
if ( j < m_min[J] ) m_min[J] = j;
if ( k < m_min[K] ) m_min[K] = k;
if ( i > m_max[I] ) m_max[I] = i;
if ( j > m_max[J] ) m_max[J] = j;
if ( k > m_max[K] ) m_max[K] = k;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool intersects( const IjkBoundingBox& box ) const
{
CVF_TIGHT_ASSERT( isValid() );
CVF_TIGHT_ASSERT( box.isValid() );
if ( m_max[I] < box.m_min[I] || m_min[I] > box.m_max[I] ) return false;
if ( m_max[J] < box.m_min[J] || m_min[J] > box.m_max[J] ) return false;
if ( m_max[K] < box.m_min[K] || m_min[K] > box.m_max[K] ) return false;
return true;
}
caf::VecIjk min() const { return caf::VecIjk( m_min[I], m_min[J], m_min[K] ); }
caf::VecIjk max() const { return caf::VecIjk( m_max[I], m_max[J], m_max[K] ); }
private:
std::array<size_t, 3> m_min;
std::array<size_t, 3> m_max;
};
//--------------------------------------------------------------------------------------------------
// Internal function
//--------------------------------------------------------------------------------------------------
// int completionPriority(const RigCompletionData& completion)
//{
// return completion.completionType() == RigCompletionData::FRACTURE ? 1 :
// completion.completionType() == RigCompletionData::FISHBONES ? 2 :
// completion.completionType() == RigCompletionData::PERFORATION ? 3 : 4;
//}
//--------------------------------------------------------------------------------------------------
// Internal function
//--------------------------------------------------------------------------------------------------
std::vector<RigCompletionData> filterCompletionsOnType( const std::vector<RigCompletionData>& completions,
const std::set<RigCompletionData::CompletionType>& includedCompletionTypes )
{
std::vector<RigCompletionData> filtered;
for ( const auto& completion : completions )
{
if ( includedCompletionTypes.count( completion.completionType() ) > 0 ) filtered.push_back( completion );
}
return filtered;
}
//--------------------------------------------------------------------------------------------------
// Internal function
//--------------------------------------------------------------------------------------------------
QString completionName( const caf::PdmObject* object )
{
auto perf = dynamic_cast<const RimPerforationInterval*>( object );
auto frac = dynamic_cast<const RimFracture*>( object );
auto fish = dynamic_cast<const RimFishbones*>( object );
QString name;
if ( perf )
name = perf->name();
else if ( frac )
name = frac->name();
else if ( fish )
name = fish->generatedName();
return name;
}
//--------------------------------------------------------------------------------------------------
/// Internal function
/// Returns the completion having highest priority.
/// Pri: 1. Fractures, 2. Fishbones, 3. Perforation intervals
//--------------------------------------------------------------------------------------------------
// RigCompletionData findCompletionByPriority(const std::vector<RigCompletionData>& completions)
//{
// std::vector<RigCompletionData> sorted = completions;
//
// std::sort(sorted.begin(), sorted.end(),
// [](const RigCompletionData& c1, const RigCompletionData& c2 )
// {
// if (completionPriority(c1) == completionPriority(c2))
// {
// return completionName(c1.sourcePdmObject()) < completionName(c2.sourcePdmObject());
// }
// return completionPriority(c1) < completionPriority(c2);
// });
// return sorted.front();
//}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RicExportLgrUi*
RicExportLgrFeature::openDialog( const QString& dialogTitle, RimEclipseCase* defaultCase, int defaultTimeStep, bool hideExportFolderField )
{
RiaApplication* app = RiaApplication::instance();
RimProject* proj = app->project();
QString startPath = app->lastUsedDialogDirectory( "LGR_EXPORT_DIR" );
if ( startPath.isEmpty() )
{
QFileInfo fi( proj->fileName() );
startPath = fi.absolutePath();
}
RicExportLgrUi* featureUi = app->project()->dialogData()->exportLgrData();
if ( featureUi->exportFolder().isEmpty() )
{
featureUi->setExportFolder( startPath );
}
if ( !featureUi->caseToApply() && !defaultCase )
{
std::vector<RimCase*> cases = app->project()->allGridCases();
for ( auto c : cases )
{
RimEclipseCase* eclipseCase = dynamic_cast<RimEclipseCase*>( c );
if ( eclipseCase != nullptr )
{
featureUi->setCase( eclipseCase );
break;
}
}
}
if ( defaultCase ) featureUi->setCase( defaultCase );
featureUi->setTimeStep( defaultTimeStep );
featureUi->hideExportFolderField( hideExportFolderField );
caf::PdmUiPropertyViewDialog propertyDialog( nullptr, featureUi, dialogTitle, "", QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
propertyDialog.resize( QSize( 300, 320 ) );
if ( propertyDialog.exec() == QDialog::Accepted && !featureUi->exportFolder().isEmpty() )
{
app->setLastUsedDialogDirectory( "LGR_EXPORT_DIR", featureUi->exportFolder() );
return featureUi;
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicExportLgrFeature::openFileForExport( const QString& folderName, const QString& fileName, QFile* exportFile )
{
QDir exportFolder = QDir( folderName );
if ( !exportFolder.exists() )
{
bool createdPath = exportFolder.mkpath( "." );
if ( createdPath ) RiaLogging::info( "Created export folder " + folderName );
}
QString filePath = exportFolder.filePath( fileName );
exportFile->setFileName( filePath );
if ( !exportFile->open( QIODevice::WriteOnly | QIODevice::Text ) )
{
auto errorMessage = QString( "Export Well Path: Could not open the file: %1" ).arg( filePath );
RiaLogging::error( errorMessage );
return false;
}
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicExportLgrFeature::writeLgrs( QTextStream& stream, const std::vector<LgrInfo>& lgrInfos )
{
for ( auto lgrInfo : lgrInfos )
{
{
RifTextDataTableFormatter formatter( stream );
formatter.comment( QString( "LGR: " ) + lgrInfo.name );
formatter.keyword( "CARFIN" );
formatter.header( { RifTextDataTableColumn( "Name" ),
RifTextDataTableColumn( "I1" ),
RifTextDataTableColumn( "I2" ),
RifTextDataTableColumn( "J1" ),
RifTextDataTableColumn( "J2" ),
RifTextDataTableColumn( "K1" ),
RifTextDataTableColumn( "K2" ),
RifTextDataTableColumn( "NX" ),
RifTextDataTableColumn( "NY" ),
RifTextDataTableColumn( "NZ" ) } );
formatter.add( lgrInfo.name );
formatter.addOneBasedCellIndex( lgrInfo.mainGridStartCell.i() );
formatter.addOneBasedCellIndex( lgrInfo.mainGridEndCell.i() );
formatter.addOneBasedCellIndex( lgrInfo.mainGridStartCell.j() );
formatter.addOneBasedCellIndex( lgrInfo.mainGridEndCell.j() );
formatter.addOneBasedCellIndex( lgrInfo.mainGridStartCell.k() );
formatter.addOneBasedCellIndex( lgrInfo.mainGridEndCell.k() );
formatter.add( lgrInfo.sizes.i() );
formatter.add( lgrInfo.sizes.j() );
formatter.add( lgrInfo.sizes.k() );
formatter.rowCompleted();
formatter.tableCompleted( "", false );
}
{
RifTextDataTableFormatter formatter( stream );
formatter.keyword( "ENDFIN" );
formatter.tableCompleted( "", true );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicExportLgrFeature::exportLgrsForWellPaths( const QString& exportFolder,
std::vector<RimWellPath*> wellPaths,
RimEclipseCase* eclipseCase,
size_t timeStep,
caf::VecIjk lgrCellCounts,
Lgr::SplitType splitType,
const std::set<RigCompletionData::CompletionType>& completionTypes,
QStringList* wellsIntersectingOtherLgrs )
{
std::vector<LgrInfo> lgrs;
lgrs = buildLgrsForWellPaths( wellPaths, eclipseCase, timeStep, lgrCellCounts, splitType, completionTypes, wellsIntersectingOtherLgrs );
for ( const auto& wellPath : wellPaths )
{
std::vector<LgrInfo> expLgrs;
for ( const auto& lgr : lgrs )
{
if ( lgr.associatedWellPathName == wellPath->name() ) expLgrs.push_back( lgr );
}
if ( !lgrs.empty() )
{
exportLgrs( exportFolder, wellPath->name(), expLgrs );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicExportLgrFeature::exportLgrs( const QString& exportFolder, const QString& wellName, const std::vector<LgrInfo>& lgrInfos )
{
if ( !lgrInfos.empty() )
{
// Export
QFile file;
QString fileName = caf::Utils::makeValidFileBasename( QString( "LGR_%1" ).arg( wellName ) ) + ".dat";
openFileForExport( exportFolder, fileName, &file );
QTextStream stream( &file );
stream.setRealNumberNotation( QTextStream::FixedNotation );
stream.setRealNumberPrecision( 2 );
writeLgrs( stream, lgrInfos );
file.close();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<LgrInfo> RicExportLgrFeature::buildLgrsForWellPaths( std::vector<RimWellPath*> wellPaths,
RimEclipseCase* eclipseCase,
size_t timeStep,
caf::VecIjk lgrCellCounts,
Lgr::SplitType splitType,
const std::set<RigCompletionData::CompletionType>& completionTypes,
QStringList* wellsIntersectingOtherLgrs )
{
std::vector<LgrInfo> lgrs;
LgrNameFactory lgrNameFactory;
wellsIntersectingOtherLgrs->clear();
bool isIntersectingOtherLgrs = false;
int firstLgrId = firstAvailableLgrId( eclipseCase->mainGrid() );
if ( splitType == Lgr::LGR_PER_CELL )
{
for ( const auto& wellPath : wellPaths )
{
auto intersectingCells = cellsIntersectingCompletions( eclipseCase, wellPath, timeStep, completionTypes, &isIntersectingOtherLgrs );
if ( intersectingCells.empty() )
{
// Find all grid cells intersected by well path
intersectingCells = allIntersectedCells( eclipseCase, wellPath );
}
auto newLgrs =
buildLgrsPerMainCell( firstLgrId + (int)lgrs.size(), eclipseCase, wellPath, intersectingCells, lgrCellCounts, lgrNameFactory );
lgrs.insert( lgrs.end(), newLgrs.begin(), newLgrs.end() );
if ( isIntersectingOtherLgrs ) wellsIntersectingOtherLgrs->push_back( wellPath->name() );
}
}
else if ( splitType == Lgr::LGR_PER_COMPLETION )
{
auto intersectingCells =
cellsIntersectingCompletions_PerCompletion( eclipseCase, wellPaths, timeStep, completionTypes, wellsIntersectingOtherLgrs );
if ( !intersectingCells.empty() )
{
auto newLgrs =
buildLgrsPerCompletion( firstLgrId + (int)lgrs.size(), eclipseCase, intersectingCells, lgrCellCounts, lgrNameFactory );
lgrs.insert( lgrs.end(), newLgrs.begin(), newLgrs.end() );
}
}
else if ( splitType == Lgr::LGR_PER_WELL )
{
for ( const auto& wellPath : wellPaths )
{
int lgrId = firstLgrId + (int)lgrs.size();
auto lgrName = lgrNameFactory.newName( "WELL", lgrId );
auto intersectingCells = cellsIntersectingCompletions( eclipseCase, wellPath, timeStep, completionTypes, &isIntersectingOtherLgrs );
if ( intersectingCells.empty() )
{
// Find all grid cells intersected by well path
intersectingCells = allIntersectedCells( eclipseCase, wellPath );
}
if ( !intersectingCells.empty() )
{
lgrs.push_back( buildLgr( lgrId, lgrName, eclipseCase, wellPath->name(), intersectingCells, lgrCellCounts ) );
}
if ( isIntersectingOtherLgrs ) wellsIntersectingOtherLgrs->push_back( wellPath->name() );
}
}
return lgrs;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<LgrInfo> RicExportLgrFeature::buildLgrsPerMainCell( int firstLgrId,
RimEclipseCase* eclipseCase,
RimWellPath* wellPath,
const std::vector<RigCompletionDataGridCell>& intersectingCells,
const caf::VecIjk& lgrSizes,
LgrNameFactory& lgrNameFactory )
{
std::vector<LgrInfo> lgrs;
int lgrId = firstLgrId;
for ( const auto& intersectionCell : intersectingCells )
{
auto lgrName = lgrNameFactory.newName( "", lgrId );
lgrs.push_back( buildLgr( lgrId++, lgrName, eclipseCase, wellPath->name(), { intersectionCell }, lgrSizes ) );
}
return lgrs;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<LgrInfo>
RicExportLgrFeature::buildLgrsPerCompletion( int firstLgrId,
RimEclipseCase* eclipseCase,
const std::map<CompletionInfo, std::vector<RigCompletionDataGridCell>>& completionInfo,
const caf::VecIjk& lgrSizesPerMainGridCell,
LgrNameFactory& lgrNameFactory )
{
std::vector<LgrInfo> lgrs;
std::vector<std::pair<CompletionInfo, IjkBoundingBox>> occupiedBbs;
for ( const auto& complInfo : completionInfo )
{
auto complCells = std::set<RigCompletionDataGridCell>( complInfo.second.begin(), complInfo.second.end() );
std::vector<RigCompletionDataGridCell> cellsUsedInBb;
while ( !complCells.empty() )
{
IjkBoundingBox maxBb;
for ( const auto& cell : complCells )
{
auto candidateBb = maxBb;
candidateBb.addCell( cell.localCellIndexI(), cell.localCellIndexJ(), cell.localCellIndexK() );
// Test bounding box
bool intersectsExistingBb = false;
for ( const auto& bb : occupiedBbs )
{
if ( candidateBb.intersects( bb.second ) )
{
intersectsExistingBb = true;
break;
}
}
if ( !intersectsExistingBb )
{
maxBb = candidateBb;
cellsUsedInBb.push_back( cell );
}
}
// If bounding box is invalid, all cells are already occupied
if ( !maxBb.isValid() ) break;
occupiedBbs.emplace_back( complInfo.first, maxBb );
// Remove cells used in bounding box
for ( const auto& cell : cellsUsedInBb )
complCells.erase( cell );
}
}
int lgrId = firstLgrId;
for ( auto complInfo : occupiedBbs )
{
auto lgrName = lgrNameFactory.newName( complInfo.first.type );
lgrs.push_back( buildLgr( lgrId++, lgrName, eclipseCase, complInfo.first.wellPathName, complInfo.second, lgrSizesPerMainGridCell ) );
}
return lgrs;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
LgrInfo RicExportLgrFeature::buildLgr( int lgrId,
const QString& lgrName,
RimEclipseCase* eclipseCase,
const QString& wellPathName,
const std::vector<RigCompletionDataGridCell>& intersectingCells,
const caf::VecIjk& lgrSizesPerMainGridCell )
{
// Find min and max IJK
auto iRange = initRange();
auto jRange = initRange();
auto kRange = initRange();
for ( const auto& cell : intersectingCells )
{
iRange.first = std::min( cell.localCellIndexI(), iRange.first );
iRange.second = std::max( cell.localCellIndexI(), iRange.second );
jRange.first = std::min( cell.localCellIndexJ(), jRange.first );
jRange.second = std::max( cell.localCellIndexJ(), jRange.second );
kRange.first = std::min( cell.localCellIndexK(), kRange.first );
kRange.second = std::max( cell.localCellIndexK(), kRange.second );
}
caf::VecIjk mainGridStartCell( iRange.first, jRange.first, kRange.first );
caf::VecIjk mainGridEndCell( iRange.second, jRange.second, kRange.second );
IjkBoundingBox boundingBox( mainGridStartCell, mainGridEndCell );
return buildLgr( lgrId, lgrName, eclipseCase, wellPathName, boundingBox, lgrSizesPerMainGridCell );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
LgrInfo RicExportLgrFeature::buildLgr( int lgrId,
const QString& lgrName,
RimEclipseCase* eclipseCase,
const QString& wellPathName,
const IjkBoundingBox& boundingBox,
const caf::VecIjk& lgrSizesPerMainGridCell )
{
caf::VecIjk lgrSizes( ( boundingBox.max().i() - boundingBox.min().i() + 1 ) * lgrSizesPerMainGridCell.i(),
( boundingBox.max().j() - boundingBox.min().j() + 1 ) * lgrSizesPerMainGridCell.j(),
( boundingBox.max().k() - boundingBox.min().k() + 1 ) * lgrSizesPerMainGridCell.k() );
return LgrInfo( lgrId, lgrName, wellPathName, lgrSizes, boundingBox.min(), boundingBox.max() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RigCompletionDataGridCell>
RicExportLgrFeature::cellsIntersectingCompletions( RimEclipseCase* eclipseCase,
const RimWellPath* wellPath,
size_t timeStep,
const std::set<RigCompletionData::CompletionType>& completionTypes,
bool* isIntersectingOtherLgrs )
{
std::vector<RigCompletionDataGridCell> cells;
const RigMainGrid* mainGrid = eclipseCase->mainGrid();
*isIntersectingOtherLgrs = false;
auto completions = eclipseCase->computeAndGetVirtualPerforationTransmissibilities();
if ( completions )
{
auto intCells = completions->multipleCompletionsPerEclipseCell( wellPath, timeStep );
for ( auto intCell : intCells )
{
const RigGridBase* grid = hostGrid( mainGrid, intCell.first );
if ( grid != mainGrid )
{
*isIntersectingOtherLgrs = true;
continue;
}
auto filteredCompletions = filterCompletionsOnType( intCell.second, completionTypes );
if ( filteredCompletions.empty() ) continue;
cells.push_back( RigCompletionDataGridCell( intCell.first, mainGrid ) );
}
}
return cells;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::map<CompletionInfo, std::vector<RigCompletionDataGridCell>>
RicExportLgrFeature::cellsIntersectingCompletions_PerCompletion( RimEclipseCase* eclipseCase,
const std::vector<RimWellPath*>& wellPaths,
size_t timeStep,
const std::set<RigCompletionData::CompletionType>& completionTypes,
QStringList* wellsIntersectingOtherLgrs )
{
const RigMainGrid* mainGrid = eclipseCase->mainGrid();
std::map<CompletionInfo, std::vector<RigCompletionDataGridCell>> completionToCells;
wellsIntersectingOtherLgrs->clear();
auto completions = eclipseCase->computeAndGetVirtualPerforationTransmissibilities();
if ( !completions ) return completionToCells;
for ( const auto& wellPath : wellPaths )
{
bool isIntersectingOtherLgrs = false;
const auto& intCells = completions->multipleCompletionsPerEclipseCell( wellPath, timeStep );
for ( const auto& intCell : intCells )
{
const RigGridBase* grid = hostGrid( mainGrid, intCell.first );
if ( grid != mainGrid )
{
isIntersectingOtherLgrs = true;
continue;
}
for ( const auto& completion : intCell.second )
{
auto complName = completionName( completion.sourcePdmObject() );
CompletionInfo ci( completion.completionType(), complName, completion.wellName() );
auto& item = completionToCells[ci];
item.push_back( RigCompletionDataGridCell( intCell.first, mainGrid ) );
}
}
if ( isIntersectingOtherLgrs ) wellsIntersectingOtherLgrs->push_back( wellPath->name() );
}
return completionToCells;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RigCompletionDataGridCell> RicExportLgrFeature::allIntersectedCells( RimEclipseCase* eclipseCase, const RimWellPath* wellPath )
{
std::vector<RigCompletionDataGridCell> cells;
const RigMainGrid* mainGrid = eclipseCase->mainGrid();
auto globalCellIndices = RigWellPathIntersectionTools::findIntersectedGlobalCellIndices( eclipseCase->eclipseCaseData(),
wellPath->wellPathGeometry()->wellPathPoints() );
for ( const auto& globalCellIndex : globalCellIndices )
{
cells.push_back( RigCompletionDataGridCell( globalCellIndex, mainGrid ) );
}
return cells;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicExportLgrFeature::isCommandEnabled() const
{
std::vector<RimWellPathCompletions*> completions = caf::selectedObjectsByTypeStrict<RimWellPathCompletions*>();
std::vector<RimWellPath*> wellPaths = caf::selectedObjectsByTypeStrict<RimWellPath*>();
return !completions.empty() || !wellPaths.empty();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicExportLgrFeature::onActionTriggered( bool isChecked )
{
std::vector<RimWellPath*> wellPaths = selectedWellPaths();
if ( wellPaths.empty() ) return;
QString dialogTitle = "Export LGR for Completions";
RimEclipseCase* defaultEclipseCase = nullptr;
int defaultTimeStep = 0;
auto activeView = dynamic_cast<RimEclipseView*>( RiaApplication::instance()->activeGridView() );
if ( activeView )
{
defaultEclipseCase = activeView->eclipseCase();
defaultTimeStep = activeView->currentTimeStep();
}
auto dialogData = openDialog( dialogTitle, defaultEclipseCase, defaultTimeStep );
if ( dialogData )
{
auto eclipseCase = dialogData->caseToApply();
auto lgrCellCounts = dialogData->lgrCellCount();
size_t timeStep = dialogData->timeStep();
QStringList wellsIntersectingOtherLgrs;
exportLgrsForWellPaths( dialogData->exportFolder(),
wellPaths,
eclipseCase,
timeStep,
lgrCellCounts,
dialogData->splitType(),
dialogData->completionTypes(),
&wellsIntersectingOtherLgrs );
if ( !wellsIntersectingOtherLgrs.empty() )
{
auto wellsList = wellsIntersectingOtherLgrs.join( ", " );
RiaLogging::errorInMessageBox( nullptr,
"LGR cells intersected",
"No export for some wells due to existing intersecting LGR(s). Affected "
"wells: " +
wellsList );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicExportLgrFeature::setupActionLook( QAction* actionToSetup )
{
actionToSetup->setText( "Export LGR for completions" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimWellPath*> RicExportLgrFeature::selectedWellPaths()
{
std::vector<RimWellPathCompletions*> selectedCompletions = caf::selectedObjectsByTypeStrict<RimWellPathCompletions*>();
std::vector<RimWellPath*> wellPaths = caf::selectedObjectsByTypeStrict<RimWellPath*>();
for ( auto completion : selectedCompletions )
{
RimWellPath* parentWellPath = completion->firstAncestorOrThisOfType<RimWellPath>();
if ( parentWellPath ) wellPaths.push_back( parentWellPath );
}
return wellPaths;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<caf::VecIjk, caf::VecIjk> mainGridCellBoundingBoxFromLgr( const RigGridBase* lgr )
{
auto mainGrid = lgr->mainGrid();
// Find min and max IJK
auto iRange = RicExportLgrFeature::initRange();
auto jRange = RicExportLgrFeature::initRange();
auto kRange = RicExportLgrFeature::initRange();
for ( size_t c = 0; c < lgr->cellCount(); c++ )
{
const auto& cell = lgr->cell( c );
size_t mainGridCellIndex = cell.mainGridCellIndex();
size_t i, j, k;
mainGrid->ijkFromCellIndex( mainGridCellIndex, &i, &j, &k );
iRange.first = std::min( i, iRange.first );
iRange.second = std::max( i, iRange.second );
jRange.first = std::min( j, jRange.first );
jRange.second = std::max( j, jRange.second );
kRange.first = std::min( k, kRange.first );
kRange.second = std::max( k, kRange.second );
}
caf::VecIjk mainGridStartCell( iRange.first, jRange.first, kRange.first );
caf::VecIjk mainGridEndCell( iRange.second, jRange.second, kRange.second );
return std::make_pair( mainGridStartCell, mainGridEndCell );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::map<QString /*wellName*/, std::vector<LgrInfo>> RicExportLgrFeature::createLgrInfoListForTemporaryLgrs( const RigMainGrid* mainGrid )
{
std::map<QString, std::vector<LgrInfo>> lgrInfosPerWell;
for ( size_t i = 0; i < mainGrid->gridCount(); i++ )
{
const auto grid = mainGrid->gridByIndex( i );
if ( !grid->isTempGrid() ) continue;
caf::VecIjk lgrSizes( grid->cellCountI(), grid->cellCountJ(), grid->cellCountK() );
std::pair<caf::VecIjk, caf::VecIjk> mainGridBoundingBox = mainGridCellBoundingBoxFromLgr( grid );
QString wellName = QString::fromStdString( grid->associatedWellPathName() );
auto& item = lgrInfosPerWell[wellName];
item.emplace_back( LgrInfo( grid->gridId(),
QString::fromStdString( grid->gridName() ),
QString::fromStdString( grid->associatedWellPathName() ),
lgrSizes,
mainGridBoundingBox.first,
mainGridBoundingBox.second ) );
}
return lgrInfosPerWell;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RicExportLgrFeature::firstAvailableLgrId( const RigMainGrid* mainGrid )
{
int gridCount = (int)mainGrid->gridCount();
int lastUsedId = 0;
for ( int i = 0; i < gridCount; i++ )
{
lastUsedId = std::max( lastUsedId, mainGrid->gridByIndex( i )->gridId() );
}
return lastUsedId + 1;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const RigGridBase* RicExportLgrFeature::hostGrid( const RigMainGrid* mainGrid, size_t reservoirCellIndex )
{
size_t dummy = 0;
return mainGrid->gridAndGridLocalIdxFromGlobalCellIdx( reservoirCellIndex, &dummy );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
LgrNameFactory::LgrNameFactory()
{
m_counters = { { RigCompletionData::CompletionType::FRACTURE, { "FRAC", 1 } },
{ RigCompletionData::CompletionType::FISHBONES, { "FB", 1 } },
{ RigCompletionData::CompletionType::PERFORATION, { "PERF", 1 } } };
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString LgrNameFactory::newName( RigCompletionData::CompletionType completionType )
{
switch ( completionType )
{
case RigCompletionData::CompletionType::FRACTURE:
case RigCompletionData::CompletionType::FISHBONES:
case RigCompletionData::CompletionType::PERFORATION:
{
auto& counter = m_counters[completionType];
QString name = counter.first + "_" + QString::number( counter.second );
counter.second++;
return name;
}
default:
return "Unknown type";
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString LgrNameFactory::newName( const QString& baseName, int number )
{
QString lgrName;
if ( baseName.isEmpty() ) lgrName = "LGR";
lgrName += baseName + "_" + QString::number( number );
return lgrName.replace( " ", "_" );
}