mirror of
https://github.com/OPM/ResInsight.git
synced 2026-09-03 20:53:13 -05:00
#14611 Nested hybrid grid: number sibling LGRs consistently
A refinement level with a single reconstructed LGR keeps the bare level name (LGR_NHG_L2). A level with several LGRs now numbers all of them with 1-based suffixes (LGR_NHG_L4_1 .. LGR_NHG_L4_N) instead of leaving the first one without a suffix.
This commit is contained in:
@@ -152,7 +152,8 @@ bool RigNestedHybridGridReconstructor::reconstruct( RigEclipseCaseData* caseData
|
||||
parentsByFactor[factor].insert( parent );
|
||||
}
|
||||
|
||||
int componentIndex = 0;
|
||||
// Collect the connected components first so the component count is known before naming.
|
||||
std::vector<std::pair<Factor, std::vector<CoarseIjk>>> components;
|
||||
for ( const auto& [factor, parents] : parentsByFactor )
|
||||
{
|
||||
std::set<CoarseIjk> remaining = parents;
|
||||
@@ -180,53 +181,60 @@ bool RigNestedHybridGridReconstructor::reconstruct( RigEclipseCaseData* caseData
|
||||
}
|
||||
}
|
||||
|
||||
CoarseIjk c0 = { INT_MAX, INT_MAX, INT_MAX };
|
||||
CoarseIjk c1 = { 0, 0, 0 };
|
||||
for ( const CoarseIjk& parent : component )
|
||||
for ( int axis = 0; axis < 3; axis++ )
|
||||
{
|
||||
c0[axis] = std::min( c0[axis], parent[axis] );
|
||||
c1[axis] = std::max( c1[axis], parent[axis] );
|
||||
}
|
||||
components.push_back( { factor, std::move( component ) } );
|
||||
}
|
||||
}
|
||||
|
||||
const cvf::Vec3st dims( (size_t)( c1[0] - c0[0] + 1 ) * factor[0],
|
||||
(size_t)( c1[1] - c0[1] + 1 ) * factor[1],
|
||||
(size_t)( c1[2] - c0[2] + 1 ) * factor[2] );
|
||||
std::vector<size_t> boxToParent( dims.x() * dims.y() * dims.z(), cvf::UNDEFINED_SIZE_T );
|
||||
for ( size_t lk = 0; lk < dims.z(); lk++ )
|
||||
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 );
|
||||
}
|
||||
for ( size_t componentIndex = 0; componentIndex < components.size(); componentIndex++ )
|
||||
{
|
||||
const auto& [factor, component] = components[componentIndex];
|
||||
|
||||
std::vector<size_t> boxToFlat( dims.x() * dims.y() * dims.z(), cvf::UNDEFINED_SIZE_T );
|
||||
for ( const CoarseIjk& parent : component )
|
||||
CoarseIjk c0 = { INT_MAX, INT_MAX, INT_MAX };
|
||||
CoarseIjk c1 = { 0, 0, 0 };
|
||||
for ( const CoarseIjk& parent : component )
|
||||
for ( int axis = 0; axis < 3; axis++ )
|
||||
{
|
||||
const auto& coordinates = coordinatesByParent[parent];
|
||||
for ( size_t f : cellsByParent[parent] )
|
||||
{
|
||||
const std::array<size_t, 3> flatIjk = { f % nx, ( f / nx ) % ny, f / ( nx * ny ) };
|
||||
size_t localIjk[3];
|
||||
for ( int axis = 0; axis < 3; axis++ )
|
||||
{
|
||||
const auto coordinateIt = std::lower_bound( coordinates[axis].begin(), coordinates[axis].end(), flatIjk[axis] );
|
||||
localIjk[axis] = (size_t)( parent[axis] - c0[axis] ) * factor[axis] +
|
||||
(size_t)( coordinateIt - coordinates[axis].begin() );
|
||||
}
|
||||
boxToFlat[naturalIndex( localIjk[0], localIjk[1], localIjk[2], dims.x(), dims.y() )] = f;
|
||||
}
|
||||
c0[axis] = std::min( c0[axis], parent[axis] );
|
||||
c1[axis] = std::max( c1[axis], parent[axis] );
|
||||
}
|
||||
|
||||
QString gridName = QString( "LGR_NHG_L%1" ).arg( level );
|
||||
if ( componentIndex > 0 ) gridName += QString( "_%1" ).arg( componentIndex );
|
||||
componentIndex++;
|
||||
buildLocalGrid( caseData, grid, nextGridId++, gridName, dims, boxToFlat, boxToParent, sourceCells );
|
||||
const cvf::Vec3st dims( (size_t)( c1[0] - c0[0] + 1 ) * factor[0],
|
||||
(size_t)( c1[1] - c0[1] + 1 ) * factor[1],
|
||||
(size_t)( c1[2] - c0[2] + 1 ) * factor[2] );
|
||||
std::vector<size_t> boxToParent( dims.x() * dims.y() * dims.z(), cvf::UNDEFINED_SIZE_T );
|
||||
for ( size_t lk = 0; lk < dims.z(); lk++ )
|
||||
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 );
|
||||
}
|
||||
|
||||
std::vector<size_t> boxToFlat( dims.x() * dims.y() * dims.z(), cvf::UNDEFINED_SIZE_T );
|
||||
for ( const CoarseIjk& parent : component )
|
||||
{
|
||||
const auto& coordinates = coordinatesByParent[parent];
|
||||
for ( size_t f : cellsByParent[parent] )
|
||||
{
|
||||
const std::array<size_t, 3> flatIjk = { f % nx, ( f / nx ) % ny, f / ( nx * ny ) };
|
||||
size_t localIjk[3];
|
||||
for ( int axis = 0; axis < 3; axis++ )
|
||||
{
|
||||
const auto coordinateIt = std::lower_bound( coordinates[axis].begin(), coordinates[axis].end(), flatIjk[axis] );
|
||||
localIjk[axis] = (size_t)( parent[axis] - c0[axis] ) * factor[axis] +
|
||||
(size_t)( coordinateIt - coordinates[axis].begin() );
|
||||
}
|
||||
boxToFlat[naturalIndex( localIjk[0], localIjk[1], localIjk[2], dims.x(), dims.y() )] = f;
|
||||
}
|
||||
}
|
||||
|
||||
// Single component on a level keeps the bare level name; several get 1-based suffixes.
|
||||
QString gridName = QString( "LGR_NHG_L%1" ).arg( level );
|
||||
if ( components.size() > 1 ) gridName += QString( "_%1" ).arg( componentIndex + 1 );
|
||||
buildLocalGrid( caseData, grid, nextGridId++, gridName, dims, boxToFlat, boxToParent, sourceCells );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -335,6 +335,44 @@ TEST_F( RigNestedHybridGridReconstructorTest, ReconstructFromOldIjk )
|
||||
EXPECT_GT( checkedL2, 0 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// LGR naming (#14611): a level with a single LGR keeps the bare level name, while a level with
|
||||
/// several LGRs numbers all of them 1..N with no bare-named sibling. All names are unique.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST_F( RigNestedHybridGridReconstructorTest, LgrNamesAreConsistent )
|
||||
{
|
||||
RigMainGrid* mainGrid = grid();
|
||||
|
||||
std::set<QString> uniqueNames;
|
||||
std::map<QString, QStringList> namesByLevel;
|
||||
for ( size_t i = 1; i < mainGrid->gridCount(); i++ )
|
||||
{
|
||||
const QString name = QString::fromStdString( mainGrid->gridByIndex( i )->gridName() );
|
||||
EXPECT_TRUE( uniqueNames.insert( name ).second ) << "Duplicate LGR name: " << name.toStdString();
|
||||
namesByLevel[name.section( '_', 0, 2 )].push_back( name ); // "LGR_NHG_L4_2" -> "LGR_NHG_L4"
|
||||
}
|
||||
|
||||
for ( const auto& [levelName, names] : namesByLevel )
|
||||
{
|
||||
if ( names.size() == 1 )
|
||||
{
|
||||
EXPECT_EQ( names.front(), levelName );
|
||||
}
|
||||
else
|
||||
{
|
||||
QStringList expected;
|
||||
for ( int n = 1; n <= names.size(); n++ )
|
||||
expected.push_back( QString( "%1_%2" ).arg( levelName ).arg( n ) );
|
||||
EXPECT_EQ( names, expected );
|
||||
}
|
||||
}
|
||||
|
||||
// The Drogon model has one LGR on levels 2 and 3 and several on level 4.
|
||||
EXPECT_EQ( namesByLevel["LGR_NHG_L2"].size(), 1 );
|
||||
EXPECT_EQ( namesByLevel["LGR_NHG_L3"].size(), 1 );
|
||||
EXPECT_GT( namesByLevel["LGR_NHG_L4"].size(), 1 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Results propagate to the reconstructed LGR cells: an active-cell-indexed STATIC result and the
|
||||
/// full-length REFINE input property (both loaded before reconstruction) must both carry, on an LGR
|
||||
|
||||
Reference in New Issue
Block a user