mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-26 07:00:17 -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
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user