mirror of
https://github.com/OPM/ResInsight.git
synced 2026-11-02 20:30:46 -06:00
Well target mapping: separate start and grow data filters
Add a start data filter used when finding starting cells. If empty, the grow filter is used. An empty grow filter allows growing anywhere.
This commit is contained in:
1 parent
da4e6e15cd
commit
19f94395a7
5 files changed
+76
-41
No files matched your search
@@ -160,7 +160,17 @@ RimWellTargetMapping::RimWellTargetMapping()
|
|||||||
"How much to increase the bounding box of the first case to cover for any grid size differences across the "
|
"How much to increase the bounding box of the first case to cover for any grid size differences across the "
|
||||||
"ensemble." );
|
"ensemble." );
|
||||||
|
|
||||||
CAF_PDM_InitFieldNoDefault( &m_dataFilter, "DataFilter", "Data Filter" );
|
// "DataFilter" is the grow filter; keeping the keyword keeps old project files working (an empty start filter falls back to it).
|
||||||
|
CAF_PDM_InitFieldNoDefault( &m_startDataFilter,
|
||||||
|
"StartDataFilter",
|
||||||
|
"Start Data Filter",
|
||||||
|
"",
|
||||||
|
"Limits where starting cells are searched for. If empty, the grow data filter is used." );
|
||||||
|
CAF_PDM_InitFieldNoDefault( &m_dataFilter,
|
||||||
|
"DataFilter",
|
||||||
|
"Grow Data Filter",
|
||||||
|
"",
|
||||||
|
"Limits where clusters can grow. If empty, clusters can grow anywhere in the model." );
|
||||||
|
|
||||||
CAF_PDM_InitFieldNoDefault( &m_ensembleStatisticsCase, "EnsembleStatisticsCase", "Ensemble Statistics Case" );
|
CAF_PDM_InitFieldNoDefault( &m_ensembleStatisticsCase, "EnsembleStatisticsCase", "Ensemble Statistics Case" );
|
||||||
|
|
||||||
@@ -226,7 +236,7 @@ QList<caf::PdmOptionItemInfo> RimWellTargetMapping::calculateValueOptions( const
|
|||||||
caf::AppEnum<RigWellTargetMapping::VolumesType>::setEnumSubset( &m_volumesType, findAvailableVolumesTypes( fc ) );
|
caf::AppEnum<RigWellTargetMapping::VolumesType>::setEnumSubset( &m_volumesType, findAvailableVolumesTypes( fc ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( fieldNeedingOptions == &m_dataFilter )
|
else if ( fieldNeedingOptions == &m_dataFilter || fieldNeedingOptions == &m_startDataFilter )
|
||||||
{
|
{
|
||||||
options.push_back( caf::PdmOptionItemInfo( "None", nullptr ) );
|
options.push_back( caf::PdmOptionItemInfo( "None", nullptr ) );
|
||||||
|
|
||||||
@@ -498,6 +508,7 @@ void RimWellTargetMapping::defineUiOrdering( QString uiConfigName, caf::PdmUiOrd
|
|||||||
auto gridEnsemble = firstAncestorOrThisOfType<RimReservoirGridEnsemble>();
|
auto gridEnsemble = firstAncestorOrThisOfType<RimReservoirGridEnsemble>();
|
||||||
|
|
||||||
auto hasEnsembleParent = firstAncestorOrThisOfType<RimEclipseCaseEnsemble>() != nullptr || gridEnsemble != nullptr;
|
auto hasEnsembleParent = firstAncestorOrThisOfType<RimEclipseCaseEnsemble>() != nullptr || gridEnsemble != nullptr;
|
||||||
|
uiOrdering.add( &m_startDataFilter );
|
||||||
uiOrdering.add( &m_dataFilter );
|
uiOrdering.add( &m_dataFilter );
|
||||||
|
|
||||||
caf::PdmUiGroup* minimumCellValuesGroup = uiOrdering.addNewGroup( "Minimum Cell Values" );
|
caf::PdmUiGroup* minimumCellValuesGroup = uiOrdering.addNewGroup( "Minimum Cell Values" );
|
||||||
@@ -560,7 +571,8 @@ RigWellTargetMapping::ClusteringLimits RimWellTargetMapping::getClusteringLimits
|
|||||||
.maxNumTargets = m_maxNumTargets,
|
.maxNumTargets = m_maxNumTargets,
|
||||||
.maxIterations = m_maxIterations,
|
.maxIterations = m_maxIterations,
|
||||||
.filterAddress = m_resultDefinition->eclipseResultAddress(),
|
.filterAddress = m_resultDefinition->eclipseResultAddress(),
|
||||||
.dataFilter = m_dataFilter() };
|
.startDataFilter = m_startDataFilter(),
|
||||||
|
.growDataFilter = m_dataFilter() };
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -107,7 +107,8 @@ private:
|
|||||||
caf::PdmField<int> m_cellCountJ;
|
caf::PdmField<int> m_cellCountJ;
|
||||||
caf::PdmField<int> m_cellCountK;
|
caf::PdmField<int> m_cellCountK;
|
||||||
|
|
||||||
caf::PdmPtrField<RimCellFilter*> m_dataFilter;
|
caf::PdmPtrField<RimCellFilter*> m_startDataFilter;
|
||||||
|
caf::PdmPtrField<RimCellFilter*> m_dataFilter; // grow filter
|
||||||
|
|
||||||
caf::PdmChildField<RimEclipseCase*> m_ensembleStatisticsCase;
|
caf::PdmChildField<RimEclipseCase*> m_ensembleStatisticsCase;
|
||||||
|
|
||||||
|
|||||||
@@ -146,45 +146,53 @@ void RigWellTargetMapping::generateCandidates( RimEclipseCase* eclips
|
|||||||
data.transmissibilityNNC = nullptr;
|
data.transmissibilityNNC = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<double> filterVector;
|
// Evaluate for each realization after opening its grid, using the mapping's time step.
|
||||||
if ( limits.dataFilter )
|
auto computeFilterVector = [&]( RimCellFilter* dataFilter ) -> std::vector<double>
|
||||||
{
|
{
|
||||||
// Evaluate for each realization after opening its grid, using the mapping's time step.
|
std::vector<double> filterVector;
|
||||||
auto visibility = RimCellFilterTools::computeReservoirCellVisibility( limits.dataFilter, eclipseCase, timeStepIdx );
|
if ( dataFilter )
|
||||||
if ( visibility.notNull() )
|
|
||||||
{
|
{
|
||||||
auto activeReservoirCellIndices =
|
auto visibility = RimCellFilterTools::computeReservoirCellVisibility( dataFilter, eclipseCase, timeStepIdx );
|
||||||
caseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices();
|
if ( visibility.notNull() )
|
||||||
const int numActiveCells = static_cast<int>( activeReservoirCellIndices.size() );
|
|
||||||
filterVector.resize( numActiveCells );
|
|
||||||
|
|
||||||
// Convert reservoir visibility to the active-cell indexing used by clustering.
|
|
||||||
#pragma omp parallel for
|
|
||||||
for ( int i = 0; i < numActiveCells; i++ )
|
|
||||||
{
|
{
|
||||||
filterVector[i] = visibility->val( activeReservoirCellIndices[i].value() ) ? 1.0 : 0.0;
|
auto activeReservoirCellIndices =
|
||||||
|
caseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices();
|
||||||
|
const int numActiveCells = static_cast<int>( activeReservoirCellIndices.size() );
|
||||||
|
filterVector.resize( numActiveCells );
|
||||||
|
|
||||||
|
// Convert reservoir visibility to the active-cell indexing used by clustering.
|
||||||
|
#pragma omp parallel for
|
||||||
|
for ( int i = 0; i < numActiveCells; i++ )
|
||||||
|
{
|
||||||
|
filterVector[i] = visibility->val( activeReservoirCellIndices[i].value() ) ? 1.0 : 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fall through to the unfiltered path below so all realizations in an ensemble get a result,
|
||||||
|
// consistent with how an invalid filterAddress is handled.
|
||||||
|
RiaLogging::warning(
|
||||||
|
std::format( "Unable to evaluate well target mapping data filter for case '{}'. Continuing without filter.",
|
||||||
|
eclipseCase->caseUserDescription().toStdString() ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if ( limits.filterAddress.isValid() )
|
||||||
{
|
{
|
||||||
// Fall through to the unfiltered path below so all realizations in an ensemble get a result,
|
resultsData->ensureKnownResultLoaded( limits.filterAddress );
|
||||||
// consistent with how an invalid filterAddress is handled.
|
filterVector = resultsData->cellScalarResults( limits.filterAddress, timeStepIdx );
|
||||||
RiaLogging::warning(
|
|
||||||
std::format( "Unable to evaluate well target mapping data filter for case '{}'. Continuing without filter.",
|
|
||||||
eclipseCase->caseUserDescription().toStdString() ) );
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if ( limits.filterAddress.isValid() )
|
|
||||||
{
|
|
||||||
resultsData->ensureKnownResultLoaded( limits.filterAddress );
|
|
||||||
filterVector = resultsData->cellScalarResults( limits.filterAddress, timeStepIdx );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable filtering if not configured: set all items to 1 (which includes all cells).
|
// Disable filtering if not configured: set all items to 1 (which includes all cells).
|
||||||
if ( filterVector.empty() || filterVector.size() != data.pressure.size() )
|
if ( filterVector.empty() || filterVector.size() != data.pressure.size() )
|
||||||
{
|
{
|
||||||
filterVector = std::vector<double>( data.pressure.size(), 1.0 );
|
filterVector = std::vector<double>( data.pressure.size(), 1.0 );
|
||||||
}
|
}
|
||||||
|
return filterVector;
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<double> growFilterVector = computeFilterVector( limits.growDataFilter );
|
||||||
|
// Without a start filter, the grow filter is used to find the starting cells.
|
||||||
|
const std::vector<double> startFilterVector = limits.startDataFilter ? computeFilterVector( limits.startDataFilter ) : growFilterVector;
|
||||||
|
|
||||||
const QString logKeyword = "RigWellTargetMapping";
|
const QString logKeyword = "RigWellTargetMapping";
|
||||||
|
|
||||||
@@ -196,7 +204,7 @@ void RigWellTargetMapping::generateCandidates( RimEclipseCase* eclips
|
|||||||
for ( int clusterId = 1; clusterId <= numClusters; clusterId++ )
|
for ( int clusterId = 1; clusterId <= numClusters; clusterId++ )
|
||||||
{
|
{
|
||||||
std::optional<caf::VecIjk0> startCell =
|
std::optional<caf::VecIjk0> startCell =
|
||||||
RigWellTargetMappingTools::findStartCell( eclipseCase, timeStepIdx, volumeType, limits, data, filterVector, clusters );
|
RigWellTargetMappingTools::findStartCell( eclipseCase, timeStepIdx, volumeType, limits, data, startFilterVector, clusters );
|
||||||
|
|
||||||
if ( startCell.has_value() )
|
if ( startCell.has_value() )
|
||||||
{
|
{
|
||||||
@@ -213,7 +221,7 @@ void RigWellTargetMapping::generateCandidates( RimEclipseCase* eclips
|
|||||||
volumeType,
|
volumeType,
|
||||||
limits,
|
limits,
|
||||||
data,
|
data,
|
||||||
filterVector,
|
growFilterVector,
|
||||||
clusters,
|
clusters,
|
||||||
clusterId,
|
clusterId,
|
||||||
timeStepIdx,
|
timeStepIdx,
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ public:
|
|||||||
int maxNumTargets;
|
int maxNumTargets;
|
||||||
int maxIterations;
|
int maxIterations;
|
||||||
RigEclipseResultAddress filterAddress;
|
RigEclipseResultAddress filterAddress;
|
||||||
RimCellFilter* dataFilter = nullptr;
|
RimCellFilter* startDataFilter = nullptr; // falls back to growDataFilter if null
|
||||||
|
RimCellFilter* growDataFilter = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void generateCandidates( RimEclipseCase* eclipseCase,
|
static void generateCandidates( RimEclipseCase* eclipseCase,
|
||||||
|
|||||||
@@ -113,6 +113,19 @@ TEST( RimWellTargetMappingTest, FilterOptionsUseOwningCase )
|
|||||||
EXPECT_EQ( caf::PdmOptionItemInfo( "Case filter", filter ).value(), options[1].value() );
|
EXPECT_EQ( caf::PdmOptionItemInfo( "Case filter", filter ).value(), options[1].value() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST( RimWellTargetMappingTest, StartFilterOptionsUseOwningCase )
|
||||||
|
{
|
||||||
|
RimEclipseResultCase eclipseCase;
|
||||||
|
auto mapping = new TestableWellTargetMapping;
|
||||||
|
eclipseCase.addWellTargetMapping( mapping );
|
||||||
|
auto filter = eclipseCase.dataFilterCollection()->addNewRangeFilter();
|
||||||
|
filter->setName( "Case filter" );
|
||||||
|
|
||||||
|
auto options = mapping->calculateValueOptions( mapping->findField( "StartDataFilter" ) );
|
||||||
|
ASSERT_EQ( 2, options.size() );
|
||||||
|
EXPECT_EQ( caf::PdmOptionItemInfo( "Case filter", filter ).value(), options[1].value() );
|
||||||
|
}
|
||||||
|
|
||||||
TEST( RimWellTargetMappingTest, FilterOptionsUseOwningEnsemble )
|
TEST( RimWellTargetMappingTest, FilterOptionsUseOwningEnsemble )
|
||||||
{
|
{
|
||||||
RimReservoirGridEnsemble ensemble;
|
RimReservoirGridEnsemble ensemble;
|
||||||
@@ -222,9 +235,9 @@ TEST_P( RimWellTargetMappingFilterTest, FiltersClustersWithoutViews )
|
|||||||
if ( useEnsemble )
|
if ( useEnsemble )
|
||||||
{
|
{
|
||||||
RigWellTargetMapping::ClusteringLimits limits{};
|
RigWellTargetMapping::ClusteringLimits limits{};
|
||||||
limits.maxNumTargets = 1;
|
limits.maxNumTargets = 1;
|
||||||
limits.maxIterations = 10;
|
limits.maxIterations = 10;
|
||||||
limits.dataFilter = filter;
|
limits.growDataFilter = filter;
|
||||||
RigFloodingSettings floodingSettings( RigFloodingSettings::FloodingType::WATER_FLOODING,
|
RigFloodingSettings floodingSettings( RigFloodingSettings::FloodingType::WATER_FLOODING,
|
||||||
0.0,
|
0.0,
|
||||||
RigFloodingSettings::FloodingType::GAS_FLOODING,
|
RigFloodingSettings::FloodingType::GAS_FLOODING,
|
||||||
|
|||||||
Reference in new issue
Block a user