Refactor: move transformIjkToSectorCoordinates and add tests.

This commit is contained in:
Kristian Bendiksen
2025-11-15 11:22:44 +01:00
parent eb02db6cc0
commit 3e668a049d
5 changed files with 220 additions and 46 deletions
@@ -516,8 +516,10 @@ std::expected<void, QString> RicExportEclipseSectorModelFeature::addBorderBounda
// Transform border cell face coordinates to sector-relative coordinates
for ( auto& face : borderCellFaces )
{
auto transformResult =
transformIjkToSectorCoordinates( face.ijk, exportSettings.min(), exportSettings.max(), exportSettings.refinement() );
auto transformResult = RigGridExportAdapter::transformIjkToSectorCoordinates( face.ijk,
exportSettings.min(),
exportSettings.max(),
exportSettings.refinement() );
if ( !transformResult )
{
@@ -530,8 +532,8 @@ std::expected<void, QString> RicExportEclipseSectorModelFeature::addBorderBounda
}
// Update the IJK coordinates to sector-relative (1-based Eclipse coordinates)
// Note: transformIjkToSectorCoordinates returns 1-based coordinates, but we need to convert back to 0-based
// for the BorderCellFace struct
// Note: RigGridExportAdapter::transformIjkToSectorCoordinates returns 1-based coordinates, but we need to convert back to
// 0-based for the BorderCellFace struct
face.ijk = cvf::Vec3st( transformResult->x() - 1, transformResult->y() - 1, transformResult->z() - 1 );
}
@@ -848,36 +850,6 @@ std::vector<RigSimWellData*>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<cvf::Vec3st, QString> RicExportEclipseSectorModelFeature::transformIjkToSectorCoordinates( const cvf::Vec3st& originalIjk,
const cvf::Vec3st& min,
const cvf::Vec3st& max,
const cvf::Vec3st& refinement )
{
// Check if original IJK is within the sector bounds
if ( originalIjk.x() < min.x() || originalIjk.x() > max.x() || originalIjk.y() < min.y() || originalIjk.y() > max.y() ||
originalIjk.z() < min.z() || originalIjk.z() > max.z() )
{
return std::unexpected( QString( "IJK coordinates (%1, %2, %3) are outside sector bounds [(%4, %5, %6), (%7, %8, %9)]" )
.arg( originalIjk.x() )
.arg( originalIjk.y() )
.arg( originalIjk.z() )
.arg( min.x() )
.arg( min.y() )
.arg( min.z() )
.arg( max.x() )
.arg( max.y() )
.arg( max.z() ) );
}
// Transform to sector-relative coordinates with refinement
// Eclipse uses 1-based indexing, so we'll return 1-based coordinates
cvf::Vec3st sectorIjk;
sectorIjk.x() = ( originalIjk.x() - min.x() ) * refinement.x() + 1;
sectorIjk.y() = ( originalIjk.y() - min.y() ) * refinement.y() + 1;
sectorIjk.z() = ( originalIjk.z() - min.z() ) * refinement.z() + 1;
return sectorIjk;
}
//--------------------------------------------------------------------------------------------------
///
@@ -970,13 +942,17 @@ std::expected<Opm::DeckRecord, QString>
// Transform K1
cvf::Vec3st origIjkK1( origI, origJ, origK1 );
auto transformResultK1 =
transformIjkToSectorCoordinates( origIjkK1, exportSettings.min(), exportSettings.max(), exportSettings.refinement() );
auto transformResultK1 = RigGridExportAdapter::transformIjkToSectorCoordinates( origIjkK1,
exportSettings.min(),
exportSettings.max(),
exportSettings.refinement() );
// Transform K2
cvf::Vec3st origIjkK2( origI, origJ, origK2 );
auto transformResultK2 =
transformIjkToSectorCoordinates( origIjkK2, exportSettings.min(), exportSettings.max(), exportSettings.refinement() );
auto transformResultK2 = RigGridExportAdapter::transformIjkToSectorCoordinates( origIjkK2,
exportSettings.min(),
exportSettings.max(),
exportSettings.refinement() );
if ( !transformResultK1 )
{
@@ -1038,7 +1014,8 @@ std::expected<Opm::DeckRecord, QString>
int origK = record.getItem( 2 ).get<int>( 0 ) - 1;
cvf::Vec3st origIjk( origI, origJ, origK );
auto transformResult = transformIjkToSectorCoordinates( origIjk, exportSettings.min(), exportSettings.max(), exportSettings.refinement() );
auto transformResult =
RigGridExportAdapter::transformIjkToSectorCoordinates( origIjk, exportSettings.min(), exportSettings.max(), exportSettings.refinement() );
if ( !transformResult )
{
@@ -1136,8 +1113,10 @@ std::expected<Opm::DeckRecord, QString>
.arg( corner2.z() + 1 ) );
}
auto transformResult1 = transformIjkToSectorCoordinates( corner1, exportSettings.min(), exportSettings.max(), exportSettings.refinement() );
auto transformResult2 = transformIjkToSectorCoordinates( corner2, exportSettings.min(), exportSettings.max(), exportSettings.refinement() );
auto transformResult1 =
RigGridExportAdapter::transformIjkToSectorCoordinates( corner1, exportSettings.min(), exportSettings.max(), exportSettings.refinement() );
auto transformResult2 =
RigGridExportAdapter::transformIjkToSectorCoordinates( corner2, exportSettings.min(), exportSettings.max(), exportSettings.refinement() );
if ( !transformResult1 )
{
@@ -95,11 +95,6 @@ private:
static std::vector<RigSimWellData*> findIntersectingWells( RimEclipseCase* eclipseCase, const cvf::Vec3st& min, const cvf::Vec3st& max );
static std::expected<cvf::Vec3st, QString> transformIjkToSectorCoordinates( const cvf::Vec3st& originalIjk,
const cvf::Vec3st& min,
const cvf::Vec3st& max,
const cvf::Vec3st& refinement );
static std::expected<Opm::DeckRecord, QString> processWelspecsRecord( const Opm::DeckRecord& record,
const std::string& wellName,
const RicExportEclipseSectorModelUi& exportSettings );
@@ -29,6 +29,8 @@
#include "cvfAssert.h"
#include "cvfStructGrid.h"
#include <QString>
//--------------------------------------------------------------------------------------------------
/// Generate refined cell corners using trilinear interpolation within the original cell
/// This ensures refined cells are strictly contained within the original cell bounds
@@ -415,3 +417,38 @@ size_t RigGridExportAdapter::totalCells() const
{
return m_refinedNI * m_refinedNJ * m_refinedNK;
}
//--------------------------------------------------------------------------------------------------
/// Transform IJK coordinates from global grid space to sector-relative space with refinement
/// Returns 1-based Eclipse coordinates
//--------------------------------------------------------------------------------------------------
std::expected<cvf::Vec3st, QString> RigGridExportAdapter::transformIjkToSectorCoordinates( const cvf::Vec3st& originalIjk,
const cvf::Vec3st& min,
const cvf::Vec3st& max,
const cvf::Vec3st& refinement )
{
// Check if original IJK is within the sector bounds
if ( originalIjk.x() < min.x() || originalIjk.x() > max.x() || originalIjk.y() < min.y() || originalIjk.y() > max.y() ||
originalIjk.z() < min.z() || originalIjk.z() > max.z() )
{
return std::unexpected( QString( "IJK coordinates (%1, %2, %3) are outside sector bounds [(%4, %5, %6), (%7, %8, %9)]" )
.arg( originalIjk.x() )
.arg( originalIjk.y() )
.arg( originalIjk.z() )
.arg( min.x() )
.arg( min.y() )
.arg( min.z() )
.arg( max.x() )
.arg( max.y() )
.arg( max.z() ) );
}
// Transform to sector-relative coordinates with refinement
// Eclipse uses 1-based indexing, so we'll return 1-based coordinates
cvf::Vec3st sectorIjk;
sectorIjk.x() = ( originalIjk.x() - min.x() ) * refinement.x() + 1;
sectorIjk.y() = ( originalIjk.y() - min.y() ) * refinement.y() + 1;
sectorIjk.z() = ( originalIjk.z() - min.z() ) * refinement.z() + 1;
return sectorIjk;
}
@@ -24,10 +24,12 @@
#include "cvfVector3.h"
#include <array>
#include <expected>
class RigEclipseCaseData;
class RigMainGrid;
class RigActiveCellInfo;
class QString;
//==================================================================================================
//
@@ -84,6 +86,12 @@ public:
cvf::Vec3st refinement() const;
bool hasRefinement() const;
// Coordinate transformation utilities
static std::expected<cvf::Vec3st, QString> transformIjkToSectorCoordinates( const cvf::Vec3st& originalIjk,
const cvf::Vec3st& min,
const cvf::Vec3st& max,
const cvf::Vec3st& refinement );
private:
// Internal methods to handle original vs refined cell access
std::array<cvf::Vec3d, 8> getOriginalCellCorners( size_t origI, size_t origJ, size_t origK ) const;
@@ -663,3 +663,158 @@ TEST( RigGridExportAdapterTest, RefinedCellsContainOriginalCorners )
EXPECT_TRUE( cornerFound[i] ) << "Original corner " << i << " was not found in any refined cell";
}
}
//--------------------------------------------------------------------------------------------------
/// Test coordinate transformation without refinement (refinement = 1,1,1)
//--------------------------------------------------------------------------------------------------
TEST( RigGridExportAdapter, TransformIjkToSectorCoordinates_NoRefinement )
{
cvf::Vec3st min( 3, 3, 1 );
cvf::Vec3st max( 10, 10, 5 );
cvf::Vec3st refinement( 1, 1, 1 );
// Point at sector min should transform to (1,1,1)
auto result1 = RigGridExportAdapter::transformIjkToSectorCoordinates( min, min, max, refinement );
EXPECT_TRUE( result1.has_value() );
EXPECT_EQ( cvf::Vec3st( 1, 1, 1 ), result1.value() );
// Point at sector max should transform correctly
auto result2 = RigGridExportAdapter::transformIjkToSectorCoordinates( max, min, max, refinement );
EXPECT_TRUE( result2.has_value() );
EXPECT_EQ( cvf::Vec3st( 8, 8, 5 ), result2.value() ); // (10-3)*1+1 = 8
// Point in middle of sector
cvf::Vec3st middle( 5, 5, 3 );
auto result3 = RigGridExportAdapter::transformIjkToSectorCoordinates( middle, min, max, refinement );
EXPECT_TRUE( result3.has_value() );
EXPECT_EQ( cvf::Vec3st( 3, 3, 3 ), result3.value() ); // (5-3)*1+1 = 3
// Test different point
cvf::Vec3st point( 7, 6, 2 );
auto result4 = RigGridExportAdapter::transformIjkToSectorCoordinates( point, min, max, refinement );
EXPECT_TRUE( result4.has_value() );
EXPECT_EQ( cvf::Vec3st( 5, 4, 2 ), result4.value() ); // (7-3)*1+1=5, (6-3)*1+1=4, (2-1)*1+1=2
}
//--------------------------------------------------------------------------------------------------
/// Test coordinate transformation with out-of-bounds detection
//--------------------------------------------------------------------------------------------------
TEST( RigGridExportAdapter, TransformIjkToSectorCoordinates_OutOfBounds )
{
cvf::Vec3st min( 3, 3, 1 );
cvf::Vec3st max( 10, 10, 5 );
cvf::Vec3st refinement( 1, 1, 1 );
// Point before sector min (X too small)
cvf::Vec3st point1( 2, 5, 3 );
auto result1 = RigGridExportAdapter::transformIjkToSectorCoordinates( point1, min, max, refinement );
EXPECT_FALSE( result1.has_value() );
EXPECT_TRUE( result1.error().contains( "outside sector bounds" ) );
// Point after sector max (X too large)
cvf::Vec3st point2( 11, 5, 3 );
auto result2 = RigGridExportAdapter::transformIjkToSectorCoordinates( point2, min, max, refinement );
EXPECT_FALSE( result2.has_value() );
// Point after sector max (Y too large)
cvf::Vec3st point3( 5, 11, 3 );
auto result3 = RigGridExportAdapter::transformIjkToSectorCoordinates( point3, min, max, refinement );
EXPECT_FALSE( result3.has_value() );
// Point after sector max (Z too large)
cvf::Vec3st point4( 5, 5, 6 );
auto result4 = RigGridExportAdapter::transformIjkToSectorCoordinates( point4, min, max, refinement );
EXPECT_FALSE( result4.has_value() );
}
//--------------------------------------------------------------------------------------------------
/// Test box transformation with refinement = (1,1,1)
/// For refinement = 1, box start and end should transform identically
//--------------------------------------------------------------------------------------------------
TEST( RigGridExportAdapter, BoxTransformation_NoRefinement )
{
cvf::Vec3st sectorMin( 3, 3, 1 );
cvf::Vec3st sectorMax( 10, 10, 5 );
cvf::Vec3st refinement( 1, 1, 1 );
// Test box from (5,5,2) to (7,7,3) in global coordinates
// Expected in sector coordinates: (3,3,2) to (5,5,3)
cvf::Vec3st boxStart( 5, 5, 2 );
cvf::Vec3st boxEnd( 7, 7, 3 );
auto startResult = RigGridExportAdapter::transformIjkToSectorCoordinates( boxStart, sectorMin, sectorMax, refinement );
auto endResult = RigGridExportAdapter::transformIjkToSectorCoordinates( boxEnd, sectorMin, sectorMax, refinement );
EXPECT_TRUE( startResult.has_value() );
EXPECT_TRUE( endResult.has_value() );
// Start: (5-3)*1+1 = 3 for each dimension
EXPECT_EQ( cvf::Vec3st( 3, 3, 2 ), startResult.value() );
// End: (7-3)*1+1 = 5 for each dimension
EXPECT_EQ( cvf::Vec3st( 5, 5, 3 ), endResult.value() );
// Verify box contains expected number of cells
// In X: 5-3+1 = 3 cells (sector cells 3,4,5)
// In Y: 5-3+1 = 3 cells
// In Z: 3-2+1 = 2 cells
size_t cellsX = endResult->x() - startResult->x() + 1;
size_t cellsY = endResult->y() - startResult->y() + 1;
size_t cellsZ = endResult->z() - startResult->z() + 1;
EXPECT_EQ( 3u, cellsX );
EXPECT_EQ( 3u, cellsY );
EXPECT_EQ( 2u, cellsZ );
}
//--------------------------------------------------------------------------------------------------
/// Test single-cell box transformation with refinement = (1,1,1)
//--------------------------------------------------------------------------------------------------
TEST( RigGridExportAdapter, SingleCellBox_NoRefinement )
{
cvf::Vec3st sectorMin( 3, 3, 1 );
cvf::Vec3st sectorMax( 10, 10, 5 );
cvf::Vec3st refinement( 1, 1, 1 );
// Single cell box at global position (5,5,2)
cvf::Vec3st singleCell( 5, 5, 2 );
auto result = RigGridExportAdapter::transformIjkToSectorCoordinates( singleCell, sectorMin, sectorMax, refinement );
EXPECT_TRUE( result.has_value() );
EXPECT_EQ( cvf::Vec3st( 3, 3, 2 ), result.value() );
// For a single-cell box, start and end are the same
auto startResult = RigGridExportAdapter::transformIjkToSectorCoordinates( singleCell, sectorMin, sectorMax, refinement );
auto endResult = RigGridExportAdapter::transformIjkToSectorCoordinates( singleCell, sectorMin, sectorMax, refinement );
EXPECT_EQ( startResult.value(), endResult.value() );
}
//--------------------------------------------------------------------------------------------------
/// Test box at sector boundaries
//--------------------------------------------------------------------------------------------------
TEST( RigGridExportAdapter, BoxAtSectorBoundaries_NoRefinement )
{
cvf::Vec3st sectorMin( 3, 3, 1 );
cvf::Vec3st sectorMax( 10, 10, 5 );
cvf::Vec3st refinement( 1, 1, 1 );
// Box spanning entire sector
auto minResult = RigGridExportAdapter::transformIjkToSectorCoordinates( sectorMin, sectorMin, sectorMax, refinement );
auto maxResult = RigGridExportAdapter::transformIjkToSectorCoordinates( sectorMax, sectorMin, sectorMax, refinement );
EXPECT_TRUE( minResult.has_value() );
EXPECT_TRUE( maxResult.has_value() );
EXPECT_EQ( cvf::Vec3st( 1, 1, 1 ), minResult.value() );
EXPECT_EQ( cvf::Vec3st( 8, 8, 5 ), maxResult.value() ); // (10-3)*1+1 = 8
// Verify sector dimensions
size_t sectorSizeX = sectorMax.x() - sectorMin.x() + 1; // 10-3+1 = 8
size_t sectorSizeY = sectorMax.y() - sectorMin.y() + 1; // 10-3+1 = 8
size_t sectorSizeZ = sectorMax.z() - sectorMin.z() + 1; // 5-1+1 = 5
EXPECT_EQ( sectorSizeX, maxResult->x() );
EXPECT_EQ( sectorSizeY, maxResult->y() );
EXPECT_EQ( sectorSizeZ, maxResult->z() );
}