Well target mapping: option to avoid growing across faults

Add AvoidFaults option that blocks cluster growth across fault faces and
fault NNCs, and generate a WELL_TARGET_FAULT_FACES result with the number
of fault faces per cell.
This commit is contained in:
Kristian Bendiksen committed 2026-10-15 12:56:18 +02:00
1 parent 19f94395a7
commit a789bdb4b4
7 files changed
+110 -1

No files matched your search

@@ -146,6 +146,8 @@ void RigWellTargetMapping::generateCandidates( RimEclipseCase* eclips
data.transmissibilityNNC = nullptr;
}
if ( limits.avoidFaults && mainGrid != nullptr ) data.isFaultNnc = RigWellTargetMappingTools::findFaultNncs( mainGrid );
// Evaluate for each realization after opening its grid, using the mapping's time step.
auto computeFilterVector = [&]( RimCellFilter* dataFilter ) -> std::vector<double>
{
@@ -245,6 +247,15 @@ void RigWellTargetMapping::generateCandidates( RimEclipseCase* eclips
QString resultName = RigWellTargetMapping::wellTargetResultName();
RigWellTargetMappingTools::createResultVector( *eclipseCase, resultName, clusters, timeStepIdx );
if ( limits.avoidFaults && mainGrid != nullptr )
{
RigWellTargetMappingTools::createResultVector( *eclipseCase,
wellTargetFaultFacesResultName(),
RigWellTargetMappingTools::countFaultFacesPerActiveCell( mainGrid,
resultsData->activeCellInfo() ),
timeStepIdx );
}
std::vector<RigWellTargetMappingTools::ClusterStatistics> statistics =
RigWellTargetMappingTools::generateStatistics( eclipseCase, data.pressure, data.permeabilityX, numClustersFound, timeStepIdx, resultName );
std::vector<double> totalPorvSoil( clusters.size(), std::numeric_limits<double>::infinity() );
@@ -520,3 +531,11 @@ QString RigWellTargetMapping::wellTargetResultName()
{
return "WELL_TARGET_NUM";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RigWellTargetMapping::wellTargetFaultFacesResultName()
{
return "WELL_TARGET_FAULT_FACES";
}
@@ -67,6 +67,7 @@ public:
double transmissibility;
int maxNumTargets;
int maxIterations;
bool avoidFaults = false; // do not grow across fault faces and fault NNCs
RigEclipseResultAddress filterAddress;
RimCellFilter* startDataFilter = nullptr; // falls back to growDataFilter if null
RimCellFilter* growDataFilter = nullptr;
@@ -102,4 +103,5 @@ public:
double expandBoundingBoxZPercent );
static QString wellTargetResultName();
static QString wellTargetFaultFacesResultName();
};
@@ -24,10 +24,12 @@
#include "RiaResultNames.h"
#include "RiaWeightedMeanCalculator.h"
#include "RigActiveCellInfo.h"
#include "RigCaseCellResultsData.h"
#include "RigCell.h"
#include "RigEclipseCaseData.h"
#include "RigEclipseResultInfo.h"
#include "RigFault.h"
#include "RigFloodingSettings.h"
#include "RigGridBase.h"
#include "RigHydrocarbonFlowTools.h"
@@ -159,6 +161,46 @@ void RigWellTargetMappingTools::assignClusterIdToCells( const RigActiveCellInfo&
}
}
//--------------------------------------------------------------------------------------------------
/// Flags the NNCs that connect cells across a fault
//--------------------------------------------------------------------------------------------------
std::vector<bool> RigWellTargetMappingTools::findFaultNncs( RigMainGrid* mainGrid )
{
if ( mainGrid->nncData() == nullptr ) return {};
std::vector<bool> isFaultNnc( mainGrid->nncData()->allConnections().size(), false );
for ( const auto& fault : mainGrid->faults() )
{
for ( size_t nncIdx : fault->connectionIndices() )
{
if ( nncIdx < isFaultNnc.size() ) isFaultNnc[nncIdx] = true;
}
}
return isFaultNnc;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<int> RigWellTargetMappingTools::countFaultFacesPerActiveCell( const RigMainGrid* mainGrid, const RigActiveCellInfo* activeCellInfo )
{
const std::vector<CellFaceType> faces =
{ CellFaceType::POS_I, CellFaceType::NEG_I, CellFaceType::POS_J, CellFaceType::NEG_J, CellFaceType::POS_K, CellFaceType::NEG_K };
std::vector<int> result( activeCellInfo->reservoirActiveCellCount(), 0 );
for ( size_t reservoirIdx = 0; reservoirIdx < activeCellInfo->reservoirCellCount(); reservoirIdx++ )
{
ActiveCellIndex resultIdx = activeCellInfo->cellResultIndex( ReservoirCellIndex( reservoirIdx ) );
if ( resultIdx.value() == cvf::UNDEFINED_SIZE_T ) continue;
for ( CellFaceType face : faces )
{
if ( mainGrid->findFaultFromCellIndexAndCellFace( reservoirIdx, face ) != nullptr ) result[resultIdx.value()]++;
}
}
return result;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -442,6 +484,8 @@ std::vector<ReservoirCellIndex> RigWellTargetMappingTools::findCandidates( RimEc
ActiveCellIndex neighborResultIndex = resultsData->activeCellInfo()->cellResultIndex( neighborResvCellIdx );
if ( neighborResultIndex.value() != cvf::UNDEFINED_SIZE_T && clusters[neighborResultIndex.value()] == 0 )
{
if ( limits.avoidFaults && mainGrid->findFaultFromCellIndexAndCellFace( cellIdx.value(), face ) != nullptr ) continue;
double permeability = data.permeabilityX[neighborResultIndex.value()];
double transmissibility = getTransmissibilityValueForFace( data.transmissibilityX,
data.transmissibilityY,
@@ -471,7 +515,8 @@ std::vector<ReservoirCellIndex> RigWellTargetMappingTools::findCandidates( RimEc
for ( auto& [cellInfo, nncResultIdx] : nncCells )
{
auto& [otherCellIdx, face] = cellInfo;
double transmissibility = data.transmissibilityNNC->at( nncResultIdx );
if ( limits.avoidFaults && nncResultIdx < data.isFaultNnc.size() && data.isFaultNnc[nncResultIdx] ) continue;
double transmissibility = data.transmissibilityNNC->at( nncResultIdx );
ActiveCellIndex otherResultIndex = resultsData->activeCellInfo()->cellResultIndex( otherCellIdx );
if ( otherResultIndex.value() == cvf::UNDEFINED_SIZE_T ) continue;
@@ -63,6 +63,7 @@ public:
std::vector<double> transmissibilityY;
std::vector<double> transmissibilityZ;
const std::vector<double>* transmissibilityNNC;
std::vector<bool> isFaultNnc; // empty if fault barriers are not used
};
class ClusterStatistics
@@ -100,6 +101,9 @@ public:
double pressure;
};
static std::vector<bool> findFaultNncs( RigMainGrid* mainGrid );
static std::vector<int> countFaultFacesPerActiveCell( const RigMainGrid* mainGrid, const RigActiveCellInfo* activeCellInfo );
static double getValueForFace( const std::vector<double>& x,
const std::vector<double>& y,
const std::vector<double>& z,