Use NNC info for well target generation and streamlines (#12279)

* Add support for NNC when growing clusters
* Trace streamlines through NNCs, too
This commit is contained in:
jonjenssen authored and GitHub committed 2025-03-21 13:00:49 +01:00
1 parent 4ccf67beb5
commit 540c8c3b36
7 files changed
+397 -196

No files matched your search

@@ -20,6 +20,8 @@
#include "RigCell.h"
#include "RigMainGrid.h"
#include "RigNNCData.h"
#include "RigNncConnection.h"
#include "RimStreamline.h"
#include "RimStreamlineDataAccess.h"
@@ -32,18 +34,22 @@
class StreamlineSeedPoint
{
public:
StreamlineSeedPoint( double rate, size_t cellIdx, cvf::StructGridInterface::FaceType faceIdx )
StreamlineSeedPoint( double rate, size_t index, RimStreamlineGenerator::CellFaceType faceIdx )
: m_rate( rate )
, m_cellIdx( cellIdx )
, m_index( index )
, m_faceIdx( faceIdx ){};
~StreamlineSeedPoint(){};
bool operator<( const StreamlineSeedPoint& other ) const { return m_rate < other.m_rate; };
bool operator<( const StreamlineSeedPoint& other ) const { return rate() < other.rate(); };
public:
double m_rate;
size_t m_cellIdx;
cvf::StructGridInterface::FaceType m_faceIdx;
double rate() const { return m_rate; };
size_t index() const { return m_index; };
RimStreamlineGenerator::CellFaceType face() const { return m_faceIdx; };
private:
double m_rate;
size_t m_index;
RimStreamlineGenerator::CellFaceType m_faceIdx;
};
//--------------------------------------------------------------------------------------------------
@@ -77,19 +83,27 @@ void RimStreamlineGenerator::generateTracer( RigCell cell, double direction, QSt
double rate = m_dataAccess->combinedFaceRate( cell, faceIdx, m_phases, direction, dominantPhase ) * direction;
if ( rate > m_flowThreshold )
{
m_seeds.push( StreamlineSeedPoint( rate, cellIdx, faceIdx ) );
m_seeds.emplace( rate, cellIdx, faceIdx );
}
}
// add any nnc connections from this cell, too
auto nncSeeds = nncCandidates( cell.mainGridCellIndex(), m_phases, direction );
for ( auto& seed : nncSeeds )
{
m_seeds.push( seed );
}
// keep adding streamlines until we have no more seeds left in our prioritized (by flow rate) list
while ( !m_seeds.empty() )
{
const size_t cellIdx = m_seeds.top().m_cellIdx;
const cvf::StructGridInterface::FaceType faceIdx = m_seeds.top().m_faceIdx;
auto& seed = m_seeds.top();
const size_t index = seed.index();
const cvf::StructGridInterface::FaceType faceIdx = seed.face();
m_seeds.pop();
RimStreamline* streamline = new RimStreamline( simWellName );
growStreamline( streamline, cellIdx, faceIdx, direction );
growStreamline( streamline, index, faceIdx, direction );
if ( direction < 0.0 ) streamline->reverse();
@@ -105,42 +119,86 @@ void RimStreamlineGenerator::generateTracer( RigCell cell, double direction, QSt
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimStreamlineGenerator::growStreamline( RimStreamline* streamline, size_t cellIdx, cvf::StructGridInterface::FaceType faceIdx, double direction )
void RimStreamlineGenerator::growStreamline( RimStreamline* streamline, size_t index, CellFaceType faceIdx, double direction )
{
// get the cell
RigCell cell = m_dataAccess->grid()->cell( cellIdx );
// get rate
double rate = 0.0;
RiaDefines::PhaseType dominantPhaseOut;
size_t currentCellIdx = 0;
RigCell currentCell;
double rate = m_dataAccess->combinedFaceRate( cell, faceIdx, m_phases, direction, dominantPhaseOut );
// if we go backwards from a producer, the rate needs to be flipped
rate *= direction;
// grow from start cell center to face center, exiting if we reach the max length
if ( !growStreamlineFromTo( streamline, cell.center(), cell.faceCenter( faceIdx ), rate, dominantPhaseOut ) ) return;
while ( rate >= m_flowThreshold )
while ( true )
{
// find next cell and entry face
cell = cell.neighborCell( faceIdx );
if ( cell.isInvalid() ) break;
faceIdx = cvf::StructGridInterface::oppositeFace( faceIdx );
// no face? -> nnc connection
if ( faceIdx == CellFaceType::NO_FACE )
{
const auto nncIdx = index;
RigConnection nnc = m_dataAccess->nncConnection( nncIdx );
// grow from given face center to cell center, exiting if we reach the max length
if ( !growStreamlineFromTo( streamline, cell.faceCenter( faceIdx ), cell.center(), rate, dominantPhaseOut ) ) break;
size_t localCellIdx1 = 0;
auto localGrid1 = m_dataAccess->grid()->gridAndGridLocalIdxFromGlobalCellIdx( nnc.c1GlobIdx(), &localCellIdx1 );
size_t localCellIdx2 = 0;
auto localGrid2 = m_dataAccess->grid()->gridAndGridLocalIdxFromGlobalCellIdx( nnc.c2GlobIdx(), &localCellIdx2 );
const size_t cellIdx = cell.gridLocalCellIndex();
// no support for LGRs, yet
if ( localGrid1->gridId() != 0 ) return;
if ( localGrid2->gridId() != 0 ) return;
if ( m_visitedCells.count( cellIdx ) > 0 ) break;
if ( m_wellCells.count( cellIdx ) > 0 ) break;
RigCell cell1 = m_dataAccess->grid()->cell( localCellIdx1 );
RigCell cell2 = m_dataAccess->grid()->cell( localCellIdx2 );
m_visitedCells.insert( cellIdx );
// get rate
rate = m_dataAccess->combinedNNCRate( nncIdx, m_phases, direction, dominantPhaseOut );
// if we go backwards from a producer, the rate needs to be flipped
rate *= direction;
// grow from cell1 center to cell2 center, exiting if we reach max length
if ( !growStreamlineFromTo( streamline, cell1.center(), cell2.center(), rate, dominantPhaseOut ) ) return;
// give up if too low rate
if ( rate < m_flowThreshold ) return;
currentCellIdx = localCellIdx2;
currentCell = cell2;
}
else
{
// get the current cell
const auto cellIdx = index;
RigCell cell = m_dataAccess->grid()->cell( cellIdx );
// get rate
rate = m_dataAccess->combinedFaceRate( cell, faceIdx, m_phases, direction, dominantPhaseOut );
// if we go backwards from a producer, the rate needs to be flipped
rate *= direction;
// grow from start cell center to face center, exiting if we reach the max length
if ( !growStreamlineFromTo( streamline, cell.center(), cell.faceCenter( faceIdx ), rate, dominantPhaseOut ) ) return;
// find next cell and entry face
currentCell = cell.neighborCell( faceIdx );
if ( currentCell.isInvalid() ) return;
faceIdx = cvf::StructGridInterface::oppositeFace( faceIdx );
// grow from given face center to neighbour cell center, exiting if we reach the max length
if ( !growStreamlineFromTo( streamline, currentCell.faceCenter( faceIdx ), currentCell.center(), rate, dominantPhaseOut ) )
break;
// give up if too low rate
if ( rate < m_flowThreshold ) return;
currentCellIdx = currentCell.gridLocalCellIndex();
}
// have we been here before?
if ( m_visitedCells.count( currentCellIdx ) > 0 ) return;
if ( m_wellCells.count( currentCellIdx ) > 0 ) return;
m_visitedCells.insert( currentCellIdx );
// find the face with max flow where we should exit the cell
cvf::StructGridInterface::FaceType exitFace = cvf::StructGridInterface::FaceType::NO_FACE;
std::map<cvf::StructGridInterface::FaceType, double> rateMap;
CellFaceType exitFace = cvf::StructGridInterface::FaceType::NO_FACE;
std::map<CellFaceType, double> rateMap;
double maxRate = 0.0;
@@ -150,7 +208,7 @@ void RimStreamlineGenerator::growStreamline( RimStreamline* streamline, size_t c
if ( face == faceIdx ) continue;
RiaDefines::PhaseType tempDominantFace;
double faceRate = m_dataAccess->combinedFaceRate( cell, face, m_phases, direction, tempDominantFace );
double faceRate = m_dataAccess->combinedFaceRate( currentCell, face, m_phases, direction, tempDominantFace );
// if we go backwards from a producer, the rate needs to be flipped
faceRate *= direction;
@@ -160,27 +218,51 @@ void RimStreamlineGenerator::growStreamline( RimStreamline* streamline, size_t c
exitFace = face;
maxRate = faceRate;
dominantPhaseOut = tempDominantFace;
index = currentCellIdx;
}
rateMap[face] = faceRate;
}
// check if we have any NNC connections here
int nncId = -1;
auto nncConnections = nncCandidates( currentCellIdx, m_phases, direction );
for ( auto& nncPoint : nncConnections )
{
RiaDefines::PhaseType tempDominantFace;
double nncRate = m_dataAccess->combinedNNCRate( nncPoint.index(), m_phases, direction, tempDominantFace ) * direction;
if ( nncRate > maxRate )
{
nncId = (int)nncPoint.index();
maxRate = nncRate;
dominantPhaseOut = tempDominantFace;
exitFace = CellFaceType::NO_FACE;
}
}
// did we find an exit?
if ( exitFace == cvf::StructGridInterface::FaceType::NO_FACE ) break;
if ( exitFace == cvf::StructGridInterface::FaceType::NO_FACE )
{
if ( nncId < 0 ) return;
index = (size_t)nncId;
for ( auto& nncPoint : nncConnections )
{
if ( nncPoint.index() == index ) continue;
m_seeds.push( nncPoint );
}
}
// add seeds for other faces with flow > threshold
for ( auto& kvp : rateMap )
{
if ( kvp.first == exitFace ) continue;
if ( kvp.second >= m_flowThreshold ) m_seeds.push( StreamlineSeedPoint( kvp.second, cell.gridLocalCellIndex(), kvp.first ) );
if ( kvp.second >= m_flowThreshold )
m_seeds.push( StreamlineSeedPoint( kvp.second, currentCell.gridLocalCellIndex(), kvp.first ) );
}
rate = maxRate;
// grow from cell center to exit face center, stopping if we reach the max point limit
if ( !growStreamlineFromTo( streamline, cell.center(), cell.faceCenter( exitFace ), rate, dominantPhaseOut ) ) break;
rate = maxRate;
faceIdx = exitFace;
}
}
@@ -218,3 +300,31 @@ bool RimStreamlineGenerator::growStreamlineFromTo( RimStreamline* streaml
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::list<StreamlineSeedPoint> RimStreamlineGenerator::nncCandidates( size_t cellIdx, std::list<RiaDefines::PhaseType> phases, double direction )
{
std::list<StreamlineSeedPoint> foundCells;
auto mainGrid = m_dataAccess->grid();
if ( mainGrid->nncData() == nullptr ) return foundCells;
auto& connections = mainGrid->nncData()->allConnections();
for ( size_t i = 0; i < connections.size(); i++ )
{
if ( connections[i].c1GlobIdx() == cellIdx )
{
RiaDefines::PhaseType domPhase;
double rate = m_dataAccess->combinedNNCRate( i, phases, direction, domPhase ) * direction;
if ( rate > m_flowThreshold )
{
foundCells.emplace_back( rate, i, cvf::StructGridInterface::FaceType::NO_FACE );
}
}
}
return foundCells;
}