mirror of
https://github.com/OPM/ResInsight.git
synced 2025-01-23 23:13:39 -06:00
970 lines
40 KiB
C++
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( " ", "_" );
|
|
}
|