mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-24 22:27:55 -05:00
#14794 Cell filter: Use original parent IJK for IJK range filters on nested hybrid grids
For nested hybrid grids, IJK range filters (I/J/K slices and IJK filters) now refer exclusively to the original (coarse) grid IJK. Nested-grid local IJK values are no longer offered, and every cell, including cells in all refinement levels, is mapped to the original IJK of its top-most parent. - Add RigNestedHybridGridLayout describing how original cells are embedded in the flat grid, shared by the LGR reconstruction and the IJK mapping - RigMainGrid: store the layout and compute the original IJK of any cell on demand by following the parent cell chain, without per-cell storage - Range filter UI: slider limits, clamping, defaults and active cell range use the original grid dimensions; grid selection is hidden - Evaluate range filters on all grids using the original IJK in the 3D view, combined filters and data filters - 3D view context menu slices use the original IJK of the picked cell - Add unit tests for the layout, the IJK mapping against OLDIJK, and slice filtering across refinement levels
This commit is contained in:
1 parent
60f96f0ebd
commit
3d8912ba6c
15 files changed
+568
-79
No files matched your search
@@ -27,6 +27,7 @@
|
||||
#include "RigCell.h"
|
||||
#include "RigEclipseCaseData.h"
|
||||
#include "RigGridBase.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigResultAccessorFactory.h"
|
||||
|
||||
#include "Rim3dOverlayInfoConfig.h"
|
||||
@@ -751,9 +752,14 @@ void RivReservoirViewPartMgr::computeFilterVisibility( RivCellSetEnum
|
||||
cvf::UByteArray indexIncludeVisibility = ( *cellVisibility );
|
||||
cvf::UByteArray indexExcludeVisibility = ( *cellVisibility );
|
||||
|
||||
// Nested hybrid grid: IJK range filters are defined on the original grid IJK (main grid filters
|
||||
// only), and every cell in every grid is evaluated using the original IJK it maps to.
|
||||
const RigMainGrid* mainGrid = grid->mainGrid();
|
||||
const bool useNestedHybridOrigIjk = mainGrid && mainGrid->nestedHybridLayout().has_value();
|
||||
|
||||
// Build cell filter for current grid
|
||||
cvf::CellRangeFilter gridCellRangeFilter;
|
||||
cellFilterColl->compoundCellRangeFilter( &gridCellRangeFilter, grid->gridIndex() );
|
||||
cellFilterColl->compoundCellRangeFilter( &gridCellRangeFilter, useNestedHybridOrigIjk ? 0 : grid->gridIndex() );
|
||||
cellFilterColl->updateCellVisibilityByIndex( &indexIncludeVisibility, &indexExcludeVisibility, grid->gridIndex() );
|
||||
|
||||
const RigLocalGrid* lgr = nullptr;
|
||||
@@ -801,8 +807,33 @@ void RivReservoirViewPartMgr::computeFilterVisibility( RivCellSetEnum
|
||||
size_t mainGridJ;
|
||||
size_t mainGridK;
|
||||
|
||||
const bool isInSubGridArea = cell.subGrid() != nullptr;
|
||||
grid->ijkFromCellIndex( cellIndex, &mainGridI, &mainGridJ, &mainGridK );
|
||||
// Nested hybrid grid: LGR cells are evaluated directly against the main grid filter, and are
|
||||
// part of a sub grid area (as their parent cell), so filters not propagated to sub grids skip them
|
||||
const bool isInSubGridArea = cell.subGrid() != nullptr || ( useNestedHybridOrigIjk && lgr != nullptr );
|
||||
|
||||
bool hasRangeIjk = true;
|
||||
if ( useNestedHybridOrigIjk )
|
||||
{
|
||||
auto originalIjk = mainGrid->nestedHybridOriginalIjk( grid, cellIndex );
|
||||
hasRangeIjk = originalIjk.has_value();
|
||||
if ( hasRangeIjk )
|
||||
{
|
||||
mainGridI = originalIjk->x();
|
||||
mainGridJ = originalIjk->y();
|
||||
mainGridK = originalIjk->z();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
grid->ijkFromCellIndex( cellIndex, &mainGridI, &mainGridJ, &mainGridK );
|
||||
}
|
||||
|
||||
// Cells without an original IJK (nested hybrid flat refined cells and padding) are never
|
||||
// selected by an IJK range filter
|
||||
const bool isRangeVisible = hasRangeIjk &&
|
||||
gridCellRangeFilter.isCellVisible( mainGridI, mainGridJ, mainGridK, isInSubGridArea );
|
||||
const bool isRangeExcluded = hasRangeIjk &&
|
||||
gridCellRangeFilter.isCellExcluded( mainGridI, mainGridJ, mainGridK, isInSubGridArea );
|
||||
|
||||
bool nativeRangeVisibility = false;
|
||||
|
||||
@@ -812,18 +843,16 @@ void RivReservoirViewPartMgr::computeFilterVisibility( RivCellSetEnum
|
||||
{
|
||||
if ( useAndOperation )
|
||||
{
|
||||
nativeRangeVisibility = indexIncludeVisibility[cellIndex] &&
|
||||
gridCellRangeFilter.isCellVisible( mainGridI, mainGridJ, mainGridK, isInSubGridArea );
|
||||
nativeRangeVisibility = indexIncludeVisibility[cellIndex] && isRangeVisible;
|
||||
}
|
||||
else
|
||||
{
|
||||
nativeRangeVisibility = indexIncludeVisibility[cellIndex] ||
|
||||
gridCellRangeFilter.isCellVisible( mainGridI, mainGridJ, mainGridK, isInSubGridArea );
|
||||
nativeRangeVisibility = indexIncludeVisibility[cellIndex] || isRangeVisible;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nativeRangeVisibility = gridCellRangeFilter.isCellVisible( mainGridI, mainGridJ, mainGridK, isInSubGridArea );
|
||||
nativeRangeVisibility = isRangeVisible;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -831,8 +860,7 @@ void RivReservoirViewPartMgr::computeFilterVisibility( RivCellSetEnum
|
||||
nativeRangeVisibility = indexIncludeVisibility[cellIndex];
|
||||
}
|
||||
|
||||
( *cellVisibility )[cellIndex] = ( visibleDueToParentGrid || nativeRangeVisibility ) &&
|
||||
!gridCellRangeFilter.isCellExcluded( mainGridI, mainGridJ, mainGridK, isInSubGridArea ) &&
|
||||
( *cellVisibility )[cellIndex] = ( visibleDueToParentGrid || nativeRangeVisibility ) && !isRangeExcluded &&
|
||||
indexExcludeVisibility[cellIndex];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "RimCellFilter.h"
|
||||
|
||||
#include "RigGridBase.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigReservoirGridTools.h"
|
||||
#include "Rim3dView.h"
|
||||
#include "RimCase.h"
|
||||
@@ -220,9 +221,24 @@ void RimCellFilter::setGridIndex( int gridIndex )
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int RimCellFilter::gridIndex() const
|
||||
{
|
||||
// The original IJK of a nested hybrid grid is defined on the main grid only
|
||||
if ( isRangeFilter() && usesNestedHybridOriginalIjk() ) return 0;
|
||||
|
||||
return m_gridIndex;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimCellFilter::usesNestedHybridOriginalIjk() const
|
||||
{
|
||||
auto eclCase = dynamic_cast<RimEclipseCase*>( sourceCase() );
|
||||
if ( !eclCase ) return false;
|
||||
|
||||
auto mainGrid = eclCase->mainGrid();
|
||||
return mainGrid && mainGrid->nestedHybridLayout().has_value();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -267,7 +283,8 @@ void RimCellFilter::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering&
|
||||
{
|
||||
m_gridIndex.uiCapability()->setUiName( "Part" );
|
||||
}
|
||||
group->add( &m_gridIndex );
|
||||
// Nested hybrid grids: range filters always use the original grid IJK, so there is no grid to choose
|
||||
if ( !( isRangeFilter() && usesNestedHybridOriginalIjk() ) ) group->add( &m_gridIndex );
|
||||
|
||||
bool readOnlyState = isFilterControlled();
|
||||
|
||||
@@ -290,7 +307,7 @@ QString RimCellFilter::modeString() const
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const cvf::StructGridInterface* RimCellFilter::selectedGrid() const
|
||||
RimCase* RimCellFilter::sourceCase() const
|
||||
{
|
||||
// Case-level data filters have no Rim3dView ancestor; fall back to m_srcCase, which the
|
||||
// owning RimDataFilterCollection / RimCombinedFilter propagates on add.
|
||||
@@ -300,6 +317,16 @@ const cvf::StructGridInterface* RimCellFilter::selectedGrid() const
|
||||
rimCase = rimView->ownerCase();
|
||||
}
|
||||
if ( !rimCase ) rimCase = m_srcCase();
|
||||
|
||||
return rimCase;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const cvf::StructGridInterface* RimCellFilter::selectedGrid() const
|
||||
{
|
||||
RimCase* rimCase = sourceCase();
|
||||
if ( !rimCase ) return nullptr;
|
||||
|
||||
int clampedIndex = gridIndex();
|
||||
@@ -348,24 +375,33 @@ void RimCellFilter::applyToCellVisibility( cvf::UByteArray* cellVisibility,
|
||||
|
||||
if ( isRangeFilter() )
|
||||
{
|
||||
// Nested hybrid grid: the filter is defined on the original grid IJK, and every cell in every
|
||||
// grid is evaluated using the original IJK it maps to (LGR cells via their parent cells).
|
||||
const RigMainGrid* mainGrid = grid->mainGrid();
|
||||
const bool useNestedHybridOrigIjk = mainGrid && mainGrid->nestedHybridLayout().has_value();
|
||||
|
||||
// updateCompundFilter is a no-op when the range filter targets a different grid than the
|
||||
// one being evaluated. Skip the cellVisibility mutation entirely so LGR cells are not
|
||||
// erroneously blanked by a main-grid-targeted filter (legacy: parentGridVisibilities).
|
||||
if ( gridIndex() != gIndx ) return;
|
||||
const int filterGridIndex = useNestedHybridOrigIjk ? 0 : gIndx;
|
||||
if ( gridIndex() != filterGridIndex ) return;
|
||||
|
||||
cvf::CellRangeFilter rf;
|
||||
updateCompundFilter( &rf, gIndx );
|
||||
updateCompundFilter( &rf, filterGridIndex );
|
||||
|
||||
const bool isSubGrid = !grid->isMainGrid();
|
||||
|
||||
for ( size_t cellIdx = 0; cellIdx < n; ++cellIdx )
|
||||
{
|
||||
size_t i = 0, j = 0, k = 0;
|
||||
if ( grid->ijkFromCellIndex( cellIdx, &i, &j, &k ) )
|
||||
std::optional<caf::VecIjk0> ijk = useNestedHybridOrigIjk ? mainGrid->nestedHybridOriginalIjk( grid, cellIdx )
|
||||
: grid->ijkFromCellIndex( cellIdx );
|
||||
|
||||
if ( ijk )
|
||||
{
|
||||
// isCellVisible returns false when there are no include ranges (pure EXCLUDE
|
||||
// mode), so also consult isCellExcluded to mark those cells as "in the set".
|
||||
if ( rf.isCellVisible( i, j, k, isSubGrid ) || rf.isCellExcluded( i, j, k, isSubGrid ) )
|
||||
if ( rf.isCellVisible( ijk->i(), ijk->j(), ijk->k(), isSubGrid ) ||
|
||||
rf.isCellExcluded( ijk->i(), ijk->j(), ijk->k(), isSubGrid ) )
|
||||
{
|
||||
inSet[cellIdx] = 1;
|
||||
}
|
||||
|
||||
@@ -87,6 +87,10 @@ public:
|
||||
void setGridIndex( int gridIndex );
|
||||
int gridIndex() const;
|
||||
|
||||
// Nested hybrid grid: IJK range filters refer exclusively to the original (coarse) grid IJK, and
|
||||
// are evaluated on all grids by mapping every cell to its original IJK.
|
||||
bool usesNestedHybridOriginalIjk() const;
|
||||
|
||||
void updateIconState();
|
||||
void updateActiveState( bool isControlled );
|
||||
|
||||
@@ -116,6 +120,7 @@ protected:
|
||||
RimEclipseCase* eclipseCase() const;
|
||||
|
||||
const cvf::StructGridInterface* selectedGrid() const;
|
||||
RimCase* sourceCase() const;
|
||||
|
||||
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override;
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ cvf::ref<cvf::UByteArray>
|
||||
|
||||
const bool isInclude = ( filter->filterMode() == RimCellFilter::INCLUDE );
|
||||
|
||||
// Nested hybrid grid: range filters use the original grid IJK and are evaluated on every grid
|
||||
const bool usesNestedHybridOriginalIjk = caseData->mainGrid()->nestedHybridLayout().has_value();
|
||||
|
||||
// Grid local masks are kept to allow propagation of parent grid visibility into LGRs
|
||||
std::vector<cvf::ref<cvf::UByteArray>> gridMasks( caseData->gridCount() );
|
||||
|
||||
@@ -61,7 +64,7 @@ cvf::ref<cvf::UByteArray>
|
||||
{
|
||||
// Range filters evaluate only on their target grid. On other grids an INCLUDE filter
|
||||
// contributes no cells, while an EXCLUDE filter removes none.
|
||||
const bool isTargetGrid = ( filter->gridIndex() == static_cast<int>( gridIndex ) );
|
||||
const bool isTargetGrid = usesNestedHybridOriginalIjk || ( filter->gridIndex() == static_cast<int>( gridIndex ) );
|
||||
if ( isTargetGrid )
|
||||
{
|
||||
filter->applyToCellVisibility( &gridMask, grid, timeStepIndex, eclipseCase );
|
||||
|
||||
@@ -22,10 +22,12 @@
|
||||
|
||||
#include "RiaApplication.h"
|
||||
#include "RigActiveCellInfo.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigReservoirGridTools.h"
|
||||
|
||||
#include "Rim3dView.h"
|
||||
#include "RimCase.h"
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimTools.h"
|
||||
|
||||
#include "cafPdmFieldScriptingCapability.h"
|
||||
@@ -135,17 +137,15 @@ void RimCellRangeFilter::fieldChangedByUi( const caf::PdmFieldHandle* changedFie
|
||||
|
||||
if ( changedField == &m_gridIndex )
|
||||
{
|
||||
const cvf::StructGridInterface* grid = selectedGrid();
|
||||
|
||||
if ( grid && grid->cellCountI() > 0 && grid->cellCountJ() > 0 && grid->cellCountK() > 0 )
|
||||
if ( auto counts = filterCellCounts() )
|
||||
{
|
||||
cellCountI = static_cast<int>( grid->cellCountI() );
|
||||
cellCountI = static_cast<int>( counts->i() );
|
||||
startIndexI = 1;
|
||||
|
||||
cellCountJ = static_cast<int>( grid->cellCountJ() );
|
||||
cellCountJ = static_cast<int>( counts->j() );
|
||||
startIndexJ = 1;
|
||||
|
||||
cellCountK = static_cast<int>( grid->cellCountK() );
|
||||
cellCountK = static_cast<int>( counts->k() );
|
||||
startIndexK = 1;
|
||||
}
|
||||
|
||||
@@ -165,19 +165,84 @@ void RimCellRangeFilter::fieldChangedByUi( const caf::PdmFieldHandle* changedFie
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimCellRangeFilter::computeAndSetValidValues()
|
||||
{
|
||||
if ( auto counts = filterCellCounts() )
|
||||
{
|
||||
cellCountI = std::clamp( cellCountI.v(), 1, static_cast<int>( counts->i() ) );
|
||||
startIndexI = std::clamp( startIndexI.v(), 1, static_cast<int>( counts->i() ) );
|
||||
|
||||
cellCountJ = std::clamp( cellCountJ.v(), 1, static_cast<int>( counts->j() ) );
|
||||
startIndexJ = std::clamp( startIndexJ.v(), 1, static_cast<int>( counts->j() ) );
|
||||
|
||||
cellCountK = std::clamp( cellCountK.v(), 1, static_cast<int>( counts->k() ) );
|
||||
startIndexK = std::clamp( startIndexK.v(), 1, static_cast<int>( counts->k() ) );
|
||||
}
|
||||
updateIconState();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::optional<cvf::Vec3st> RimCellRangeFilter::filterCellCounts() const
|
||||
{
|
||||
if ( usesNestedHybridOriginalIjk() )
|
||||
{
|
||||
auto eclCase = dynamic_cast<RimEclipseCase*>( sourceCase() );
|
||||
if ( eclCase && eclCase->mainGrid() && eclCase->mainGrid()->nestedHybridLayout() )
|
||||
{
|
||||
return eclCase->mainGrid()->nestedHybridLayout()->originalCellCounts();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const cvf::StructGridInterface* grid = selectedGrid();
|
||||
if ( grid && grid->cellCountI() > 0 && grid->cellCountJ() > 0 && grid->cellCountK() > 0 )
|
||||
{
|
||||
cellCountI = std::clamp( cellCountI.v(), 1, static_cast<int>( grid->cellCountI() ) );
|
||||
startIndexI = std::clamp( startIndexI.v(), 1, static_cast<int>( grid->cellCountI() ) );
|
||||
|
||||
cellCountJ = std::clamp( cellCountJ.v(), 1, static_cast<int>( grid->cellCountJ() ) );
|
||||
startIndexJ = std::clamp( startIndexJ.v(), 1, static_cast<int>( grid->cellCountJ() ) );
|
||||
|
||||
cellCountK = std::clamp( cellCountK.v(), 1, static_cast<int>( grid->cellCountK() ) );
|
||||
startIndexK = std::clamp( startIndexK.v(), 1, static_cast<int>( grid->cellCountK() ) );
|
||||
return cvf::Vec3st( grid->cellCountI(), grid->cellCountJ(), grid->cellCountK() );
|
||||
}
|
||||
updateIconState();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::optional<RigBoundingBoxIjk<caf::VecIjk0>> RimCellRangeFilter::activeCellIjkBoundingBox() const
|
||||
{
|
||||
auto rimView = firstAncestorOrThisOfType<Rim3dView>();
|
||||
auto actCellInfo = RigReservoirGridTools::activeCellInfo( rimView );
|
||||
if ( !actCellInfo ) return std::nullopt;
|
||||
|
||||
const cvf::StructGridInterface* mainGrid = RigReservoirGridTools::mainGrid( m_srcCase );
|
||||
if ( !mainGrid || selectedGrid() != mainGrid ) return std::nullopt;
|
||||
|
||||
if ( !usesNestedHybridOriginalIjk() ) return actCellInfo->ijkBoundingBox();
|
||||
|
||||
// Nested hybrid grid: bounding box of the original IJK of all active cells (including LGR cells)
|
||||
auto rigMainGrid = dynamic_cast<const RigMainGrid*>( mainGrid );
|
||||
if ( !rigMainGrid ) return std::nullopt;
|
||||
|
||||
caf::VecIjk0 min = caf::VecIjk0::UNDEFINED;
|
||||
caf::VecIjk0 max = caf::VecIjk0::ZERO;
|
||||
bool hasCells = false;
|
||||
for ( ReservoirCellIndex reservoirCellIndex : actCellInfo->activeReservoirCellIndices() )
|
||||
{
|
||||
size_t localCellIndex = 0;
|
||||
auto grid = rigMainGrid->gridAndGridLocalIdxFromGlobalCellIdx( reservoirCellIndex.value(), &localCellIndex );
|
||||
auto ijk = rigMainGrid->nestedHybridOriginalIjk( grid, localCellIndex );
|
||||
if ( ijk )
|
||||
{
|
||||
for ( int axis = 0; axis < 3; axis++ )
|
||||
{
|
||||
min[axis] = std::min( min[axis], ( *ijk )[axis] );
|
||||
max[axis] = std::max( max[axis], ( *ijk )[axis] );
|
||||
}
|
||||
hasCells = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !hasCells ) return std::nullopt;
|
||||
|
||||
return RigBoundingBoxIjk<caf::VecIjk0>( min, max );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -224,22 +289,14 @@ std::map<QString, std::vector<caf::PdmFieldHandle*>> RimCellRangeFilter::quickAc
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimCellRangeFilter::setDefaultValues( int sliceDirection, int defaultSlice )
|
||||
{
|
||||
const cvf::StructGridInterface* grid = selectedGrid();
|
||||
auto counts = filterCellCounts();
|
||||
if ( !counts ) return;
|
||||
|
||||
if ( !grid ) return;
|
||||
|
||||
auto rimView = firstAncestorOrThisOfType<Rim3dView>();
|
||||
auto actCellInfo = RigReservoirGridTools::activeCellInfo( rimView );
|
||||
|
||||
const cvf::StructGridInterface* mainGrid = RigReservoirGridTools::mainGrid( m_srcCase );
|
||||
|
||||
if ( grid == mainGrid && actCellInfo )
|
||||
if ( auto bbox = activeCellIjkBoundingBox() )
|
||||
{
|
||||
const auto& bbox = actCellInfo->ijkBoundingBox();
|
||||
|
||||
// Adjust to Eclipse indexing
|
||||
auto min = bbox.min().toOneBased();
|
||||
auto max = bbox.max().toOneBased();
|
||||
auto min = bbox->min().toOneBased();
|
||||
auto max = bbox->max().toOneBased();
|
||||
|
||||
startIndexI = static_cast<int>( min.x() );
|
||||
startIndexJ = static_cast<int>( min.y() );
|
||||
@@ -253,9 +310,9 @@ void RimCellRangeFilter::setDefaultValues( int sliceDirection, int defaultSlice
|
||||
startIndexI = 1;
|
||||
startIndexJ = 1;
|
||||
startIndexK = 1;
|
||||
cellCountI = static_cast<int>( grid->cellCountI() );
|
||||
cellCountJ = static_cast<int>( grid->cellCountJ() );
|
||||
cellCountK = static_cast<int>( grid->cellCountK() );
|
||||
cellCountI = static_cast<int>( counts->x() );
|
||||
cellCountJ = static_cast<int>( counts->y() );
|
||||
cellCountK = static_cast<int>( counts->z() );
|
||||
}
|
||||
|
||||
switch ( sliceDirection )
|
||||
@@ -288,24 +345,23 @@ void RimCellRangeFilter::defineEditorAttribute( const caf::PdmFieldHandle* field
|
||||
return;
|
||||
}
|
||||
|
||||
const cvf::StructGridInterface* grid = selectedGrid();
|
||||
|
||||
if ( !grid ) return;
|
||||
auto counts = filterCellCounts();
|
||||
if ( !counts ) return;
|
||||
|
||||
if ( field == &startIndexI || field == &cellCountI )
|
||||
{
|
||||
myAttr->m_minimum = 1;
|
||||
myAttr->m_maximum = static_cast<int>( grid->cellCountI() );
|
||||
myAttr->m_maximum = static_cast<int>( counts->x() );
|
||||
}
|
||||
else if ( field == &startIndexJ || field == &cellCountJ )
|
||||
{
|
||||
myAttr->m_minimum = 1;
|
||||
myAttr->m_maximum = static_cast<int>( grid->cellCountJ() );
|
||||
myAttr->m_maximum = static_cast<int>( counts->y() );
|
||||
}
|
||||
else if ( field == &startIndexK || field == &cellCountK )
|
||||
{
|
||||
myAttr->m_minimum = 1;
|
||||
myAttr->m_maximum = static_cast<int>( grid->cellCountK() );
|
||||
myAttr->m_maximum = static_cast<int>( counts->z() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,22 +372,13 @@ void RimCellRangeFilter::defineUiOrdering( QString uiConfigName, caf::PdmUiOrder
|
||||
{
|
||||
RimCellFilter::defineUiOrdering( uiConfigName, uiOrdering );
|
||||
|
||||
const cvf::StructGridInterface* grid = selectedGrid();
|
||||
|
||||
const cvf::StructGridInterface* mainGrid = RigReservoirGridTools::mainGrid( m_srcCase );
|
||||
|
||||
auto rimView = firstAncestorOrThisOfType<Rim3dView>();
|
||||
auto actCellInfo = RigReservoirGridTools::activeCellInfo( rimView );
|
||||
|
||||
bool isRadialGrid = isMainGridRadial();
|
||||
|
||||
if ( grid == mainGrid && actCellInfo )
|
||||
if ( auto bbox = activeCellIjkBoundingBox() )
|
||||
{
|
||||
const auto& bbox = actCellInfo->ijkBoundingBox();
|
||||
|
||||
// Adjust to Eclipse indexing
|
||||
auto min = bbox.min().toOneBased();
|
||||
auto max = bbox.max().toOneBased();
|
||||
auto min = bbox->min().toOneBased();
|
||||
auto max = bbox->max().toOneBased();
|
||||
|
||||
QString label;
|
||||
|
||||
@@ -409,7 +456,7 @@ void RimCellRangeFilter::updateCompundFilter( cvf::CellRangeFilter* cellRangeFil
|
||||
{
|
||||
CAF_ASSERT( cellRangeFilter );
|
||||
|
||||
if ( gridIndex != m_gridIndex ) return;
|
||||
if ( gridIndex != this->gridIndex() ) return;
|
||||
|
||||
if ( filterMode() == RimCellFilter::INCLUDE )
|
||||
{
|
||||
|
||||
@@ -23,7 +23,12 @@
|
||||
#include "QuickAccess/RimFieldQuickAccessInterface.h"
|
||||
#include "RimCellFilter.h"
|
||||
|
||||
#include "RigBoundingBoxIjk.h"
|
||||
|
||||
#include "cafPdmFieldCvfVec3d.h"
|
||||
#include "cafVecIjk.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
class RigGridBase;
|
||||
class RigMainGrid;
|
||||
@@ -74,6 +79,13 @@ private:
|
||||
void computeAndSetValidValues();
|
||||
bool isMainGridRadial() const;
|
||||
|
||||
// Cell counts of the grid the filter IJK refers to: the original grid for nested hybrid grids,
|
||||
// otherwise the selected grid. Returns nullopt if no valid grid is available.
|
||||
std::optional<cvf::Vec3st> filterCellCounts() const;
|
||||
|
||||
// Active cell IJK bounding box (0-based) in the filter IJK space, available for the main grid only
|
||||
std::optional<RigBoundingBoxIjk<caf::VecIjk0>> activeCellIjkBoundingBox() const;
|
||||
|
||||
caf::PdmField<QString> m_labelI;
|
||||
caf::PdmField<QString> m_labelJ;
|
||||
caf::PdmField<QString> m_labelK;
|
||||
|
||||
@@ -86,7 +86,7 @@ void RimUserDefinedFilter::updateCompundFilter( cvf::CellRangeFilter* cellRangeF
|
||||
{
|
||||
CAF_ASSERT( cellRangeFilter );
|
||||
|
||||
if ( gridIndex != m_gridIndex ) return;
|
||||
if ( gridIndex != this->gridIndex() ) return;
|
||||
|
||||
if ( filterMode() == RimCellFilter::INCLUDE )
|
||||
{
|
||||
|
||||
@@ -89,6 +89,7 @@ set(SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigNonUniformRefinement.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigNestedHybridGridReconstructor.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigNestedHybridGridResultTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigNestedHybridGridLayout.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_SOURCE_FILES ${SOURCE_GROUP_SOURCE_FILES})
|
||||
@@ -423,6 +423,49 @@ const std::map<size_t, size_t>& RigMainGrid::nestedHybridCoarseParents() const
|
||||
return m_nestedHybridCoarseParents;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigMainGrid::setNestedHybridLayout( const RigNestedHybridGridLayout& layout )
|
||||
{
|
||||
m_nestedHybridLayout = layout;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::optional<RigNestedHybridGridLayout>& RigMainGrid::nestedHybridLayout() const
|
||||
{
|
||||
return m_nestedHybridLayout;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Computed on demand: LGR cells walk their parent cell chain up to the main grid, and the flat main
|
||||
/// grid IJK is converted using the layout. No per-cell storage is needed.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::optional<caf::VecIjk0> RigMainGrid::nestedHybridOriginalIjk( const RigGridBase* grid, size_t gridLocalCellIndex ) const
|
||||
{
|
||||
if ( !m_nestedHybridLayout || !grid ) return std::nullopt;
|
||||
|
||||
size_t cellIndex = gridLocalCellIndex;
|
||||
while ( !grid->isMainGrid() )
|
||||
{
|
||||
auto lgr = dynamic_cast<const RigLocalGrid*>( grid );
|
||||
if ( !lgr || cellIndex >= lgr->cellCount() ) return std::nullopt;
|
||||
|
||||
cellIndex = lgr->cell( cellIndex ).parentCellIndex();
|
||||
grid = lgr->parentGrid();
|
||||
if ( !grid || cellIndex == cvf::UNDEFINED_SIZE_T ) return std::nullopt;
|
||||
}
|
||||
|
||||
if ( grid != this || cellIndex >= cellCount() ) return std::nullopt;
|
||||
|
||||
std::optional<caf::VecIjk0> flatIjk = ijkFromCellIndex( cellIndex );
|
||||
if ( !flatIjk ) return std::nullopt;
|
||||
|
||||
return m_nestedHybridLayout->originalIjkFromFlatIjk( *flatIjk );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "RigCell.h"
|
||||
#include "RigGridBase.h"
|
||||
#include "RigLocalGrid.h"
|
||||
#include "RigNestedHybridGridLayout.h"
|
||||
|
||||
#include "cvfBoundingBox.h"
|
||||
#include "cvfCollection.h"
|
||||
@@ -31,6 +32,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
class RigActiveCellInfo;
|
||||
@@ -80,6 +82,16 @@ public:
|
||||
void setNestedHybridCoarseParents( const std::map<size_t, size_t>& cellToCoarseParent );
|
||||
const std::map<size_t, size_t>& nestedHybridCoarseParents() const;
|
||||
|
||||
// Nested hybrid grid: index layout of the original (coarse) grid embedded in the flat main grid.
|
||||
// Not set for other grids.
|
||||
void setNestedHybridLayout( const RigNestedHybridGridLayout& layout );
|
||||
const std::optional<RigNestedHybridGridLayout>& nestedHybridLayout() const;
|
||||
|
||||
// Nested hybrid grid: the original (coarse) IJK of a cell in any grid of this main grid. LGR cells
|
||||
// are followed through their parent cells (recursively) up to the main grid. Returns nullopt if
|
||||
// this is not a nested hybrid grid, or the cell has no original IJK (refined flat cells, padding).
|
||||
std::optional<caf::VecIjk0> nestedHybridOriginalIjk( const RigGridBase* grid, size_t gridLocalCellIndex ) const;
|
||||
|
||||
RigNNCData* nncData();
|
||||
|
||||
void setFaults( const cvf::Collection<RigFault>& faults );
|
||||
@@ -164,8 +176,9 @@ private:
|
||||
cvf::Collection<RigLocalGrid> m_localGrids; ///< List of all the LGR's in this reservoir
|
||||
std::vector<size_t> m_gridIdToIndexMapping; ///< Mapping from LGR Id to index.
|
||||
|
||||
std::map<size_t, size_t> m_nestedHybridLgrSourceCells; ///< LGR cell global index -> source flat cell global index
|
||||
std::map<size_t, size_t> m_nestedHybridCoarseParents; ///< flat cell global index -> parent coarse cell natural index
|
||||
std::map<size_t, size_t> m_nestedHybridLgrSourceCells; ///< LGR cell global index -> source flat cell global index
|
||||
std::map<size_t, size_t> m_nestedHybridCoarseParents; ///< flat cell global index -> parent coarse cell natural index
|
||||
std::optional<RigNestedHybridGridLayout> m_nestedHybridLayout;
|
||||
|
||||
cvf::Collection<RigFault> m_faults;
|
||||
cvf::ref<RigNNCData> m_nncData;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026- Equinor 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 "RigNestedHybridGridLayout.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigNestedHybridGridLayout::RigNestedHybridGridLayout( const cvf::Vec3st& originalCellCounts, size_t kFactor )
|
||||
: m_originalCellCounts( originalCellCounts )
|
||||
, m_kFactor( std::max( kFactor, size_t( 1 ) ) )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const cvf::Vec3st& RigNestedHybridGridLayout::originalCellCounts() const
|
||||
{
|
||||
return m_originalCellCounts;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigNestedHybridGridLayout::kFactor() const
|
||||
{
|
||||
return m_kFactor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::optional<caf::VecIjk0> RigNestedHybridGridLayout::originalIjkFromFlatIjk( const caf::VecIjk0& flatIjk ) const
|
||||
{
|
||||
const size_t ok = flatIjk.z() / m_kFactor;
|
||||
if ( flatIjk.x() >= m_originalCellCounts.x() || flatIjk.y() >= m_originalCellCounts.y() || ok >= m_originalCellCounts.z() )
|
||||
return std::nullopt;
|
||||
|
||||
return caf::VecIjk0( flatIjk.x(), flatIjk.y(), ok );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
caf::VecIjk0 RigNestedHybridGridLayout::flatIjkFromOriginalIjk( const caf::VecIjk0& originalIjk ) const
|
||||
{
|
||||
return caf::VecIjk0( originalIjk.x(), originalIjk.y(), originalIjk.z() * m_kFactor );
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026- Equinor 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.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cafVecIjk.h"
|
||||
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
//==================================================================================================
|
||||
/// Index layout of a nested hybrid grid (see RigNestedHybridGridReconstructor).
|
||||
///
|
||||
/// The flat main grid embeds each original (coarse) cell (OI, OJ, OK) (0-based) at the flat position
|
||||
/// (OI, OJ, OK * kFactor), where kFactor is the global K refinement factor. Flat columns outside the
|
||||
/// original I/J extent hold the scattered refined cells (or padding) and have no original IJK.
|
||||
///
|
||||
/// This class is the single place defining that convention, used both when reconstructing the LGRs
|
||||
/// and when mapping cells back to their original IJK (e.g. for IJK range filters).
|
||||
//==================================================================================================
|
||||
class RigNestedHybridGridLayout
|
||||
{
|
||||
public:
|
||||
RigNestedHybridGridLayout( const cvf::Vec3st& originalCellCounts, size_t kFactor );
|
||||
|
||||
const cvf::Vec3st& originalCellCounts() const;
|
||||
size_t kFactor() const;
|
||||
|
||||
// Original IJK of a flat main grid cell, or nullopt if the flat cell lies outside the original grid.
|
||||
std::optional<caf::VecIjk0> originalIjkFromFlatIjk( const caf::VecIjk0& flatIjk ) const;
|
||||
|
||||
// Flat main grid IJK of the (host) cell representing an original cell.
|
||||
caf::VecIjk0 flatIjkFromOriginalIjk( const caf::VecIjk0& originalIjk ) const;
|
||||
|
||||
private:
|
||||
cvf::Vec3st m_originalCellCounts;
|
||||
size_t m_kFactor;
|
||||
};
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "RigLocalGrid.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigNNCData.h"
|
||||
#include "RigNestedHybridGridLayout.h"
|
||||
#include "RigNestedHybridGridResultTools.h"
|
||||
#include "RigNncConnection.h"
|
||||
|
||||
@@ -94,6 +95,8 @@ bool RigNestedHybridGridReconstructor::reconstruct( RigEclipseCaseData* caseData
|
||||
}
|
||||
const size_t kFactor = ( coarseNz > 0 && nz % (size_t)coarseNz == 0 ) ? nz / (size_t)coarseNz : 1;
|
||||
|
||||
const RigNestedHybridGridLayout layout( cvf::Vec3st( (size_t)coarseNx, (size_t)coarseNy, (size_t)coarseNz ), kFactor );
|
||||
|
||||
// Group all refined cells (REFINE > 1) by level, and record each refined cell's parent COARSE
|
||||
// cell natural index (for the volume-weighted QC aggregate).
|
||||
std::map<int, std::vector<size_t>> cellsByLevel;
|
||||
@@ -206,11 +209,12 @@ bool RigNestedHybridGridReconstructor::reconstruct( RigEclipseCaseData* caseData
|
||||
for ( size_t lj = 0; lj < dims.y(); lj++ )
|
||||
for ( size_t li = 0; li < dims.x(); li++ )
|
||||
{
|
||||
size_t local = naturalIndex( li, lj, lk, dims.x(), dims.y() );
|
||||
size_t oi = (size_t)c0[0] + li / factor[0];
|
||||
size_t oj = (size_t)c0[1] + lj / factor[1];
|
||||
size_t ok = (size_t)c0[2] + lk / factor[2];
|
||||
boxToParent[local] = naturalIndex( oi - 1, oj - 1, ( ok - 1 ) * kFactor, nx, ny );
|
||||
size_t local = naturalIndex( li, lj, lk, dims.x(), dims.y() );
|
||||
size_t oi = (size_t)c0[0] + li / factor[0];
|
||||
size_t oj = (size_t)c0[1] + lj / factor[1];
|
||||
size_t ok = (size_t)c0[2] + lk / factor[2];
|
||||
caf::VecIjk0 host = layout.flatIjkFromOriginalIjk( caf::VecIjk0( oi - 1, oj - 1, ok - 1 ) );
|
||||
boxToParent[local] = naturalIndex( host.x(), host.y(), host.z(), nx, ny );
|
||||
}
|
||||
|
||||
std::vector<size_t> boxToFlat( dims.x() * dims.y() * dims.z(), cvf::UNDEFINED_SIZE_T );
|
||||
@@ -242,6 +246,7 @@ bool RigNestedHybridGridReconstructor::reconstruct( RigEclipseCaseData* caseData
|
||||
|
||||
grid->setNestedHybridLgrSourceCells( sourceCells );
|
||||
grid->setNestedHybridCoarseParents( coarseParents );
|
||||
grid->setNestedHybridLayout( layout );
|
||||
|
||||
updateActiveCellInfo( caseData, sourceCells );
|
||||
extendFullLengthResults( caseData, origCellCount, sourceCells );
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
#include "RigNestedHybridGridResultTools.h"
|
||||
#include "RigNncConnection.h"
|
||||
|
||||
#include "RimCellFilterTools.h"
|
||||
#include "RimCellRangeFilter.h"
|
||||
#include "RimEclipseResultCase.h"
|
||||
|
||||
#include <QDir>
|
||||
@@ -783,3 +785,158 @@ TEST_F( RigNestedHybridGridReconstructorTest, PerLevelSumAggregate )
|
||||
}
|
||||
EXPECT_GE( verified, 1 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// The reconstruction records the original (coarse) grid layout: 15x24x12 coarse cells, with each
|
||||
/// coarse K layer spanning 8 flat K layers (96 / 12).
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST_F( RigNestedHybridGridReconstructorTest, NestedHybridLayout )
|
||||
{
|
||||
const auto& layout = grid()->nestedHybridLayout();
|
||||
ASSERT_TRUE( layout.has_value() );
|
||||
|
||||
EXPECT_EQ( layout->originalCellCounts(), cvf::Vec3st( 15, 24, 12 ) );
|
||||
EXPECT_EQ( layout->kFactor(), 8u );
|
||||
|
||||
auto host = layout->flatIjkFromOriginalIjk( caf::VecIjk0( 3, 4, 5 ) );
|
||||
EXPECT_EQ( host, caf::VecIjk0( 3, 4, 40 ) );
|
||||
|
||||
auto original = layout->originalIjkFromFlatIjk( caf::VecIjk0( 3, 4, 47 ) );
|
||||
ASSERT_TRUE( original.has_value() );
|
||||
EXPECT_EQ( *original, caf::VecIjk0( 3, 4, 5 ) );
|
||||
|
||||
// Flat columns outside the original I/J extent hold refined cells or padding
|
||||
EXPECT_FALSE( layout->originalIjkFromFlatIjk( caf::VecIjk0( 15, 0, 0 ) ).has_value() );
|
||||
EXPECT_FALSE( layout->originalIjkFromFlatIjk( caf::VecIjk0( 0, 24, 0 ) ).has_value() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// The original IJK computed on demand (layout + parent cell chain) must agree with the OLDIJK
|
||||
/// sidecar: for every reconstructed LGR cell, for every unrefined coarse cell, and the scattered
|
||||
/// flat refined cells must have no original IJK.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST_F( RigNestedHybridGridReconstructorTest, OriginalIjkFollowsOldIjk )
|
||||
{
|
||||
RigMainGrid* mainGrid = grid();
|
||||
|
||||
auto oldIjk0 = []( size_t flat )
|
||||
{ return caf::VecIjk0( (size_t)( s_input.oldI[flat] - 1 ), (size_t)( s_input.oldJ[flat] - 1 ), (size_t)( s_input.oldK[flat] - 1 ) ); };
|
||||
|
||||
// LGR cells: original IJK equals OLDIJK of the flat cell they were built from
|
||||
size_t lgrChecked = 0, lgrMismatch = 0;
|
||||
for ( const auto& [lgrGlobal, flat] : mainGrid->nestedHybridLgrSourceCells() )
|
||||
{
|
||||
size_t localIdx = 0;
|
||||
auto lgr = mainGrid->gridAndGridLocalIdxFromGlobalCellIdx( lgrGlobal, &localIdx );
|
||||
ASSERT_FALSE( lgr->isMainGrid() );
|
||||
|
||||
auto original = mainGrid->nestedHybridOriginalIjk( lgr, localIdx );
|
||||
if ( !original || *original != oldIjk0( flat ) ) lgrMismatch++;
|
||||
lgrChecked++;
|
||||
}
|
||||
EXPECT_GT( lgrChecked, 0u );
|
||||
EXPECT_EQ( lgrMismatch, 0u );
|
||||
|
||||
// Main grid cells
|
||||
const size_t nx = mainGrid->cellCountI();
|
||||
const size_t ny = mainGrid->cellCountJ();
|
||||
|
||||
size_t coarseChecked = 0, coarseMismatch = 0, refinedWithIjk = 0;
|
||||
for ( size_t f = 0; f < mainGrid->cellCount(); f++ )
|
||||
{
|
||||
auto original = mainGrid->nestedHybridOriginalIjk( mainGrid, f );
|
||||
|
||||
if ( s_input.refine[f] > 1 )
|
||||
{
|
||||
// Scattered refined cells (now represented by LGR cells) have no original IJK
|
||||
if ( original ) refinedWithIjk++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( s_input.oldI[f] < 1 ) continue;
|
||||
|
||||
const caf::VecIjk0 flatIjk( f % nx, ( f / nx ) % ny, f / ( nx * ny ) );
|
||||
const caf::VecIjk0 expected = oldIjk0( f );
|
||||
if ( flatIjk != grid()->nestedHybridLayout()->flatIjkFromOriginalIjk( expected ) ) continue; // padding
|
||||
|
||||
if ( !original || *original != expected ) coarseMismatch++;
|
||||
coarseChecked++;
|
||||
}
|
||||
EXPECT_GT( coarseChecked, 0u );
|
||||
EXPECT_EQ( coarseMismatch, 0u );
|
||||
EXPECT_EQ( refinedWithIjk, 0u );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// IJK range filters on a nested hybrid grid refer to the original grid IJK. A slice selects every
|
||||
/// cell mapped to the original slice - unrefined cells and the cells of all refinement levels.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST_F( RigNestedHybridGridReconstructorTest, RangeFilterUsesOriginalIjk )
|
||||
{
|
||||
RigMainGrid* mainGrid = grid();
|
||||
s_resultCase->setReservoirData( s_reservoir.p() );
|
||||
|
||||
RimCellRangeFilter rangeFilter;
|
||||
rangeFilter.setCase( s_resultCase.get() );
|
||||
EXPECT_TRUE( rangeFilter.usesNestedHybridOriginalIjk() );
|
||||
|
||||
// A filter targeting an LGR is redirected to the original grid
|
||||
rangeFilter.setGridIndex( 2 );
|
||||
EXPECT_EQ( rangeFilter.gridIndex(), 0 );
|
||||
|
||||
// Original IJK level of every reconstructed LGR cell (via its source flat cell)
|
||||
std::map<size_t, int> lgrLevel; // LGR global cell index -> refinement level
|
||||
for ( const auto& [lgrGlobal, flat] : mainGrid->nestedHybridLgrSourceCells() )
|
||||
lgrLevel[lgrGlobal] = s_input.refine[flat];
|
||||
|
||||
struct Slice
|
||||
{
|
||||
int axis;
|
||||
int index; // 1-based original index
|
||||
};
|
||||
|
||||
for ( const Slice& slice : { Slice{ 0, 10 }, Slice{ 1, 12 }, Slice{ 2, 5 } } )
|
||||
{
|
||||
rangeFilter.startIndexI = 1;
|
||||
rangeFilter.startIndexJ = 1;
|
||||
rangeFilter.startIndexK = 1;
|
||||
rangeFilter.cellCountI = 15;
|
||||
rangeFilter.cellCountJ = 24;
|
||||
rangeFilter.cellCountK = 12;
|
||||
if ( slice.axis == 0 ) std::tie( rangeFilter.startIndexI.v(), rangeFilter.cellCountI.v() ) = std::make_pair( slice.index, 1 );
|
||||
if ( slice.axis == 1 ) std::tie( rangeFilter.startIndexJ.v(), rangeFilter.cellCountJ.v() ) = std::make_pair( slice.index, 1 );
|
||||
if ( slice.axis == 2 ) std::tie( rangeFilter.startIndexK.v(), rangeFilter.cellCountK.v() ) = std::make_pair( slice.index, 1 );
|
||||
|
||||
auto visibility = RimCellFilterTools::computeReservoirCellVisibility( &rangeFilter, s_resultCase.get(), 0 );
|
||||
ASSERT_TRUE( visibility.notNull() );
|
||||
ASSERT_EQ( visibility->size(), mainGrid->totalCellCount() );
|
||||
|
||||
// Every LGR cell is visible exactly when its OLDIJK lies in the slice
|
||||
size_t mismatch = 0;
|
||||
std::set<int> visibleLevels;
|
||||
const std::array oldIjk = { &s_input.oldI, &s_input.oldJ, &s_input.oldK };
|
||||
for ( const auto& [lgrGlobal, flat] : mainGrid->nestedHybridLgrSourceCells() )
|
||||
{
|
||||
const bool expected = ( *oldIjk[slice.axis] )[flat] == slice.index;
|
||||
const bool visible = visibility->val( lgrGlobal ) != 0;
|
||||
if ( expected != visible ) mismatch++;
|
||||
if ( visible ) visibleLevels.insert( lgrLevel[lgrGlobal] );
|
||||
}
|
||||
EXPECT_EQ( mismatch, 0u ) << "axis " << slice.axis;
|
||||
|
||||
// Main grid cells: only cells within the original columns, mapping to the slice, are visible
|
||||
size_t mainVisible = 0, mainWrong = 0;
|
||||
for ( size_t f = 0; f < mainGrid->cellCount(); f++ )
|
||||
{
|
||||
if ( !visibility->val( f ) ) continue;
|
||||
mainVisible++;
|
||||
auto original = mainGrid->nestedHybridOriginalIjk( mainGrid, f );
|
||||
if ( !original || ( *original )[slice.axis] != (size_t)( slice.index - 1 ) ) mainWrong++;
|
||||
}
|
||||
EXPECT_GT( mainVisible, 0u ) << "axis " << slice.axis;
|
||||
EXPECT_EQ( mainWrong, 0u ) << "axis " << slice.axis;
|
||||
|
||||
// The slices are chosen to cross more than one refinement level
|
||||
EXPECT_GE( visibleLevels.size(), 2u ) << "axis " << slice.axis;
|
||||
}
|
||||
}
|
||||
@@ -385,20 +385,39 @@ void RiuViewerCommands::displayContextMenu( QMouseEvent* event )
|
||||
size_t i, j, k;
|
||||
ijkFromCellIndex( mainOrComparisonView, m_currentGridIdx, m_currentCellIndex, &i, &j, &k );
|
||||
|
||||
size_t sliceGridIdx = m_currentGridIdx;
|
||||
|
||||
// Nested hybrid grid: slices always refer to the original grid IJK on the main grid
|
||||
if ( auto eclipseView = dynamic_cast<RimEclipseView*>( mainOrComparisonView );
|
||||
eclipseView && eclipseView->eclipseCase() && eclipseView->eclipseCase()->eclipseCaseData() )
|
||||
{
|
||||
const RigEclipseCaseData* caseData = eclipseView->eclipseCase()->eclipseCaseData();
|
||||
if ( const RigMainGrid* mainGrid = caseData->mainGrid(); mainGrid && mainGrid->nestedHybridLayout() )
|
||||
{
|
||||
if ( auto originalIjk = mainGrid->nestedHybridOriginalIjk( caseData->grid( m_currentGridIdx ), m_currentCellIndex ) )
|
||||
{
|
||||
i = originalIjk->i();
|
||||
j = originalIjk->j();
|
||||
k = originalIjk->k();
|
||||
}
|
||||
sliceGridIdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList iSliceList;
|
||||
iSliceList.push_back( 0 );
|
||||
iSliceList.push_back( std::max( static_cast<int>( i + 1 ), 1 ) );
|
||||
iSliceList.push_back( static_cast<int>( m_currentGridIdx ) );
|
||||
iSliceList.push_back( static_cast<int>( sliceGridIdx ) );
|
||||
|
||||
QVariantList jSliceList;
|
||||
jSliceList.push_back( 1 );
|
||||
jSliceList.push_back( std::max( static_cast<int>( j + 1 ), 1 ) );
|
||||
jSliceList.push_back( static_cast<int>( m_currentGridIdx ) );
|
||||
jSliceList.push_back( static_cast<int>( sliceGridIdx ) );
|
||||
|
||||
QVariantList kSliceList;
|
||||
kSliceList.push_back( 2 );
|
||||
kSliceList.push_back( std::max( static_cast<int>( k + 1 ), 1 ) );
|
||||
kSliceList.push_back( static_cast<int>( m_currentGridIdx ) );
|
||||
kSliceList.push_back( static_cast<int>( sliceGridIdx ) );
|
||||
|
||||
menuBuilder.subMenuStart( "Range Filter", QIcon( ":/CellFilter_Range.png" ) );
|
||||
|
||||
|
||||
Reference in new issue
Block a user