mirror of
https://github.com/OPM/ResInsight.git
synced 2026-08-27 05:37:21 -05:00
Replace the monolithic RigNonUniformRefinement class with a proper polymorphic hierarchy: RigRefinement (abstract base), RigNoRefinement (identity), RigUniformRefinement (O(1) uniform), and RigNonUniformRefinement (per-cell custom fractions). Ownership uses std::unique_ptr<RigRefinement>, consumers receive const RigRefinement&. This eliminates the misleading class name, removes effectiveRefinement() indirection, and gives each refinement mode an appropriately optimized implementation.
696 lines
33 KiB
C++
696 lines
33 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2025 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 "gtest/gtest.h"
|
|
|
|
#include "RiaPorosityModel.h"
|
|
#include "RiaResultNames.h"
|
|
#include "RiaTestDataDirectory.h"
|
|
|
|
#include "RifEclipseInputFileTools.h"
|
|
#include "RifReaderEclipseOutput.h"
|
|
|
|
#include "RimEclipseResultCase.h"
|
|
|
|
#include "RigActiveCellInfo.h"
|
|
#include "RigCaseCellResultsData.h"
|
|
#include "RigEclipseCaseData.h"
|
|
#include "RigEclipseCaseDataTools.h"
|
|
#include "RigEclipseResultAddress.h"
|
|
#include "RigEclipseResultTools.h"
|
|
#include "RigGridExportAdapter.h"
|
|
#include "RigMainGrid.h"
|
|
#include "RigNoRefinement.h"
|
|
#include "RigSimulationInputTool.h"
|
|
#include "Well/RigSimWellData.h"
|
|
|
|
#include "cvfBoundingBox.h"
|
|
|
|
#include <QDir>
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
TEST( RigEclipseCaseDataToolsTest, WellBoundingBoxWithBruggeModel )
|
|
{
|
|
// Load the BRUGGE test model using RifReaderEclipseOutput
|
|
QDir baseFolder( TEST_MODEL_DIR );
|
|
bool subFolderExists = baseFolder.cd( "Case_with_10_timesteps/Real0" );
|
|
ASSERT_TRUE( subFolderExists ) << "Could not find test model directory";
|
|
|
|
QString filename( "BRUGGE_0000.EGRID" );
|
|
QString filePath = baseFolder.absoluteFilePath( filename );
|
|
ASSERT_TRUE( QFile::exists( filePath ) ) << "BRUGGE test model file does not exist: " << filePath.toStdString();
|
|
|
|
std::unique_ptr<RimEclipseResultCase> resultCase( new RimEclipseResultCase );
|
|
cvf::ref<RigEclipseCaseData> eclipseCase = new RigEclipseCaseData( resultCase.get() );
|
|
|
|
cvf::ref<RifReaderEclipseOutput> readerInterfaceEcl = new RifReaderEclipseOutput;
|
|
bool success = readerInterfaceEcl->open( filePath, eclipseCase.p() );
|
|
ASSERT_TRUE( success ) << "Could not load BRUGGE test model";
|
|
|
|
eclipseCase->mainGrid()->computeCachedData();
|
|
|
|
// Find the P19-02 well
|
|
auto wells = eclipseCase->wellResults();
|
|
const RigSimWellData* testWell = nullptr;
|
|
|
|
for ( const auto& well : wells )
|
|
{
|
|
if ( well->m_wellName == "P19-02" )
|
|
{
|
|
testWell = well.p();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// List available wells for debugging if P19-02 is not found
|
|
QString availableWells;
|
|
for ( const auto& well : wells )
|
|
{
|
|
availableWells += well->m_wellName + ", ";
|
|
}
|
|
|
|
ASSERT_TRUE( testWell != nullptr ) << "Could not find P19-02 well in BRUGGE model. Available wells: " << availableWells.toStdString();
|
|
|
|
// Test domain coordinates bounding box
|
|
int timeStep = 0;
|
|
bool isAutoDetectingBranches = true;
|
|
bool isUsingCellCenterForPipe = true;
|
|
|
|
cvf::BoundingBox domainBB = RigEclipseCaseDataTools::wellBoundingBoxInDomainCoords( eclipseCase.p(),
|
|
testWell,
|
|
timeStep,
|
|
isAutoDetectingBranches,
|
|
isUsingCellCenterForPipe );
|
|
|
|
ASSERT_TRUE( domainBB.isValid() ) << "Domain bounding box should be valid";
|
|
|
|
cvf::Vec3d minPt = domainBB.min();
|
|
cvf::Vec3d maxPt = domainBB.max();
|
|
|
|
ASSERT_LE( minPt.x(), maxPt.x() );
|
|
ASSERT_LE( minPt.y(), maxPt.y() );
|
|
ASSERT_LE( minPt.z(), maxPt.z() );
|
|
|
|
// Test IJK coordinates bounding box
|
|
auto [minIjk, maxIjk] =
|
|
RigEclipseCaseDataTools::wellBoundingBoxIjk( eclipseCase.p(), testWell, timeStep, isAutoDetectingBranches, isUsingCellCenterForPipe );
|
|
|
|
ASSERT_NE( minIjk, caf::VecIjk0::UNDEFINED ) << "Min IJK should be valid";
|
|
ASSERT_NE( maxIjk, caf::VecIjk0::UNDEFINED ) << "Max IJK should be valid";
|
|
|
|
// Sanity checks
|
|
ASSERT_LE( minIjk.x(), maxIjk.x() ) << "Min I should be <= Max I";
|
|
ASSERT_LE( minIjk.y(), maxIjk.y() ) << "Min J should be <= Max J";
|
|
ASSERT_LE( minIjk.z(), maxIjk.z() ) << "Min K should be <= Max K";
|
|
|
|
// Expected values from visual inspection
|
|
ASSERT_EQ( minIjk.x(), 51 );
|
|
ASSERT_EQ( minIjk.y(), 42 );
|
|
ASSERT_EQ( minIjk.z(), 2 );
|
|
|
|
ASSERT_EQ( maxIjk.x(), 51 );
|
|
ASSERT_EQ( maxIjk.y(), 42 );
|
|
ASSERT_EQ( maxIjk.z(), 4 );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
TEST( RigEclipseCaseDataToolsTest, MultipleWellsBoundingBoxWithBruggeModel )
|
|
{
|
|
// Load the BRUGGE test model using RifReaderEclipseOutput
|
|
QDir baseFolder( TEST_MODEL_DIR );
|
|
bool subFolderExists = baseFolder.cd( "Case_with_10_timesteps/Real0" );
|
|
ASSERT_TRUE( subFolderExists ) << "Could not find test model directory";
|
|
|
|
QString filename( "BRUGGE_0000.EGRID" );
|
|
QString filePath = baseFolder.absoluteFilePath( filename );
|
|
ASSERT_TRUE( QFile::exists( filePath ) ) << "BRUGGE test model file does not exist: " << filePath.toStdString();
|
|
|
|
std::unique_ptr<RimEclipseResultCase> resultCase( new RimEclipseResultCase );
|
|
cvf::ref<RigEclipseCaseData> eclipseCase = new RigEclipseCaseData( resultCase.get() );
|
|
|
|
cvf::ref<RifReaderEclipseOutput> readerInterfaceEcl = new RifReaderEclipseOutput;
|
|
bool success = readerInterfaceEcl->open( filePath, eclipseCase.p() );
|
|
ASSERT_TRUE( success ) << "Could not load BRUGGE test model";
|
|
|
|
eclipseCase->mainGrid()->computeCachedData();
|
|
|
|
// Find wells P19-03 and P20-03
|
|
auto wells = eclipseCase->wellResults();
|
|
std::vector<const RigSimWellData*> testWells;
|
|
|
|
for ( const auto& well : wells )
|
|
{
|
|
if ( well->m_wellName == "P19-03" || well->m_wellName == "P20-03" )
|
|
{
|
|
testWells.push_back( well.p() );
|
|
}
|
|
}
|
|
|
|
// List available wells for debugging
|
|
QString availableWells;
|
|
for ( const auto& well : wells )
|
|
{
|
|
availableWells += well->m_wellName + ", ";
|
|
}
|
|
|
|
ASSERT_EQ( testWells.size(), 2 ) << "Could not find both P19-03 and P20-03 wells in BRUGGE model. Available wells: "
|
|
<< availableWells.toStdString();
|
|
|
|
// Test multiple wells IJK coordinates bounding box
|
|
int timeStep = 0;
|
|
bool isAutoDetectingBranches = true;
|
|
bool isUsingCellCenterForPipe = true;
|
|
|
|
auto [minIjk, maxIjk] =
|
|
RigEclipseCaseDataTools::wellsBoundingBoxIjk( eclipseCase.p(), testWells, timeStep, isAutoDetectingBranches, isUsingCellCenterForPipe );
|
|
|
|
ASSERT_NE( minIjk, caf::VecIjk0::UNDEFINED ) << "Min IJK should be valid";
|
|
ASSERT_NE( maxIjk, caf::VecIjk0::UNDEFINED ) << "Max IJK should be valid";
|
|
|
|
// Expected values from visual inspection
|
|
ASSERT_EQ( minIjk.x(), 44 );
|
|
ASSERT_EQ( minIjk.y(), 41 );
|
|
ASSERT_EQ( minIjk.z(), 5 );
|
|
|
|
ASSERT_EQ( maxIjk.x(), 51 );
|
|
ASSERT_EQ( maxIjk.y(), 42 );
|
|
ASSERT_EQ( maxIjk.z(), 7 );
|
|
|
|
// Calculate total cells in bounding box
|
|
size_t totalCells = ( maxIjk.x() - minIjk.x() + 1 ) * ( maxIjk.y() - minIjk.y() + 1 ) * ( maxIjk.z() - minIjk.z() + 1 );
|
|
|
|
// Verify that we have a reasonable number of cells (48 based on the actual grid structure)
|
|
ASSERT_EQ( totalCells, 48 ) << "Expected 48 cells in bounding box for P19-03 and P20-03";
|
|
|
|
// Sanity checks
|
|
ASSERT_LE( minIjk.x(), maxIjk.x() ) << "Min I should be <= Max I";
|
|
ASSERT_LE( minIjk.y(), maxIjk.y() ) << "Min J should be <= Max J";
|
|
ASSERT_LE( minIjk.z(), maxIjk.z() ) << "Min K should be <= Max K";
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
TEST( RigEclipseCaseDataToolsTest, ExpandBoundingBoxIjkWithPadding )
|
|
{
|
|
// Load the BRUGGE test model using RifReaderEclipseOutput
|
|
QDir baseFolder( TEST_MODEL_DIR );
|
|
bool subFolderExists = baseFolder.cd( "Case_with_10_timesteps/Real0" );
|
|
ASSERT_TRUE( subFolderExists ) << "Could not find test model directory";
|
|
|
|
QString filename( "BRUGGE_0000.EGRID" );
|
|
QString filePath = baseFolder.absoluteFilePath( filename );
|
|
ASSERT_TRUE( QFile::exists( filePath ) ) << "BRUGGE test model file does not exist: " << filePath.toStdString();
|
|
|
|
std::unique_ptr<RimEclipseResultCase> resultCase( new RimEclipseResultCase );
|
|
cvf::ref<RigEclipseCaseData> eclipseCase = new RigEclipseCaseData( resultCase.get() );
|
|
|
|
cvf::ref<RifReaderEclipseOutput> readerInterfaceEcl = new RifReaderEclipseOutput;
|
|
bool success = readerInterfaceEcl->open( filePath, eclipseCase.p() );
|
|
ASSERT_TRUE( success ) << "Could not load BRUGGE test model";
|
|
|
|
eclipseCase->mainGrid()->computeCachedData();
|
|
|
|
// Get grid dimensions for boundary testing
|
|
auto mainGrid = eclipseCase->mainGrid();
|
|
size_t gridCellCountI = mainGrid->cellCountI();
|
|
size_t gridCellCountJ = mainGrid->cellCountJ();
|
|
size_t gridCellCountK = mainGrid->cellCountK();
|
|
|
|
// Test case 1: Normal expansion with padding (using safe values that won't hit grid boundaries)
|
|
caf::VecIjk0 originalMin( 20, 20, 3 );
|
|
caf::VecIjk0 originalMax( 22, 22, 4 );
|
|
size_t padding = 3;
|
|
|
|
auto [expandedMin1, expandedMax1] = RigEclipseCaseDataTools::expandBoundingBoxIjk( eclipseCase.p(), originalMin, originalMax, padding );
|
|
|
|
ASSERT_NE( expandedMin1, caf::VecIjk0::UNDEFINED ) << "Expanded min should be valid";
|
|
ASSERT_NE( expandedMax1, caf::VecIjk0::UNDEFINED ) << "Expanded max should be valid";
|
|
|
|
// Check expansion worked correctly (these values should not hit grid boundaries)
|
|
ASSERT_EQ( expandedMin1.x(), originalMin.x() - padding ) << "Min I should be reduced by padding";
|
|
ASSERT_EQ( expandedMin1.y(), originalMin.y() - padding ) << "Min J should be reduced by padding";
|
|
ASSERT_EQ( expandedMin1.z(), originalMin.z() - padding ) << "Min K should be reduced by padding";
|
|
|
|
ASSERT_EQ( expandedMax1.x(), originalMax.x() + padding ) << "Max I should be increased by padding";
|
|
ASSERT_EQ( expandedMax1.y(), originalMax.y() + padding ) << "Max J should be increased by padding";
|
|
ASSERT_EQ( expandedMax1.z(), originalMax.z() + padding ) << "Max K should be increased by padding";
|
|
|
|
// Test case 2: Expansion at grid boundary - ensure min never goes below 0
|
|
caf::VecIjk0 boundaryMin( 1, 1, 1 );
|
|
caf::VecIjk0 boundaryMax( 3, 3, 3 );
|
|
size_t largePadding = 5;
|
|
|
|
auto [expandedMin2, expandedMax2] =
|
|
RigEclipseCaseDataTools::expandBoundingBoxIjk( eclipseCase.p(), boundaryMin, boundaryMax, largePadding );
|
|
|
|
ASSERT_NE( expandedMin2, caf::VecIjk0::UNDEFINED ) << "Boundary expanded min should be valid";
|
|
ASSERT_NE( expandedMax2, caf::VecIjk0::UNDEFINED ) << "Boundary expanded max should be valid";
|
|
|
|
// Ensure min coordinates never go negative (should be clamped to 0)
|
|
ASSERT_EQ( expandedMin2.x(), 0 ) << "Min I should be clamped to 0";
|
|
ASSERT_EQ( expandedMin2.y(), 0 ) << "Min J should be clamped to 0";
|
|
ASSERT_EQ( expandedMin2.z(), 0 ) << "Min K should be clamped to 0";
|
|
|
|
// Test case 3: Expansion at grid upper boundary - ensure max never exceeds grid dimensions
|
|
caf::VecIjk0 upperBoundaryMin( gridCellCountI - 3, gridCellCountJ - 3, gridCellCountK - 3 );
|
|
caf::VecIjk0 upperBoundaryMax( gridCellCountI - 1, gridCellCountJ - 1, gridCellCountK - 1 );
|
|
|
|
auto [expandedMin3, expandedMax3] =
|
|
RigEclipseCaseDataTools::expandBoundingBoxIjk( eclipseCase.p(), upperBoundaryMin, upperBoundaryMax, largePadding );
|
|
|
|
ASSERT_NE( expandedMin3, caf::VecIjk0::UNDEFINED ) << "Upper boundary expanded min should be valid";
|
|
ASSERT_NE( expandedMax3, caf::VecIjk0::UNDEFINED ) << "Upper boundary expanded max should be valid";
|
|
|
|
// Ensure max coordinates never exceed grid dimensions (should be clamped to grid size - 1)
|
|
ASSERT_EQ( expandedMax3.x(), gridCellCountI - 1 ) << "Max I should be clamped to grid size - 1";
|
|
ASSERT_EQ( expandedMax3.y(), gridCellCountJ - 1 ) << "Max J should be clamped to grid size - 1";
|
|
ASSERT_EQ( expandedMax3.z(), gridCellCountK - 1 ) << "Max K should be clamped to grid size - 1";
|
|
|
|
// General sanity checks for all test cases
|
|
ASSERT_LE( expandedMin1.x(), expandedMax1.x() ) << "Expanded Min I should be <= Max I";
|
|
ASSERT_LE( expandedMin1.y(), expandedMax1.y() ) << "Expanded Min J should be <= Max J";
|
|
ASSERT_LE( expandedMin1.z(), expandedMax1.z() ) << "Expanded Min K should be <= Max K";
|
|
|
|
ASSERT_LE( expandedMin2.x(), expandedMax2.x() ) << "Boundary expanded Min I should be <= Max I";
|
|
ASSERT_LE( expandedMin2.y(), expandedMax2.y() ) << "Boundary expanded Min J should be <= Max J";
|
|
ASSERT_LE( expandedMin2.z(), expandedMax2.z() ) << "Boundary expanded Min K should be <= Max K";
|
|
|
|
ASSERT_LE( expandedMin3.x(), expandedMax3.x() ) << "Upper boundary expanded Min I should be <= Max I";
|
|
ASSERT_LE( expandedMin3.y(), expandedMax3.y() ) << "Upper boundary expanded Min J should be <= Max J";
|
|
ASSERT_LE( expandedMin3.z(), expandedMax3.z() ) << "Upper boundary expanded Min K should be <= Max K";
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
TEST( RigEclipseCaseDataToolsTest, CreateVisibilityFromIjkBounds )
|
|
{
|
|
// Load the BRUGGE test model using RifReaderEclipseOutput
|
|
QDir baseFolder( TEST_MODEL_DIR );
|
|
bool subFolderExists = baseFolder.cd( "Case_with_10_timesteps/Real0" );
|
|
ASSERT_TRUE( subFolderExists ) << "Could not find test model directory";
|
|
|
|
QString filename( "BRUGGE_0000.EGRID" );
|
|
QString filePath = baseFolder.absoluteFilePath( filename );
|
|
ASSERT_TRUE( QFile::exists( filePath ) ) << "BRUGGE test model file does not exist: " << filePath.toStdString();
|
|
|
|
std::unique_ptr<RimEclipseResultCase> resultCase( new RimEclipseResultCase );
|
|
cvf::ref<RigEclipseCaseData> eclipseCase = new RigEclipseCaseData( resultCase.get() );
|
|
|
|
cvf::ref<RifReaderEclipseOutput> readerInterfaceEcl = new RifReaderEclipseOutput;
|
|
bool success = readerInterfaceEcl->open( filePath, eclipseCase.p() );
|
|
ASSERT_TRUE( success ) << "Could not load BRUGGE test model";
|
|
|
|
ASSERT_NE( eclipseCase->mainGrid(), nullptr ) << "Main grid should not be null";
|
|
ASSERT_GT( eclipseCase->mainGrid()->cellCount(), 0 ) << "Grid should contain cells";
|
|
|
|
// Get grid dimensions for test validation
|
|
size_t gridCellCountI = eclipseCase->mainGrid()->cellCountI();
|
|
size_t gridCellCountJ = eclipseCase->mainGrid()->cellCountJ();
|
|
size_t gridCellCountK = eclipseCase->mainGrid()->cellCountK();
|
|
size_t totalCellCount = eclipseCase->mainGrid()->cellCount();
|
|
|
|
// Test case 1: Basic visibility generation with small bounds
|
|
caf::VecIjk0 minIjk( 5, 5, 5 );
|
|
caf::VecIjk0 maxIjk( 10, 10, 8 );
|
|
|
|
auto visibility = RigEclipseCaseDataTools::createVisibilityFromIjkBounds( eclipseCase.p(), minIjk, maxIjk );
|
|
|
|
ASSERT_FALSE( visibility.isNull() ) << "Visibility array should not be null";
|
|
ASSERT_EQ( visibility->size(), totalCellCount ) << "Visibility array should match total cell count";
|
|
|
|
// Count visible cells and verify they match expected IJK range
|
|
size_t expectedVisibleCells = ( maxIjk.x() - minIjk.x() + 1 ) * ( maxIjk.y() - minIjk.y() + 1 ) * ( maxIjk.z() - minIjk.z() + 1 );
|
|
size_t actualVisibleCells = 0;
|
|
|
|
for ( size_t i = 0; i < totalCellCount; ++i )
|
|
{
|
|
if ( visibility->val( i ) )
|
|
{
|
|
actualVisibleCells++;
|
|
}
|
|
}
|
|
|
|
ASSERT_EQ( actualVisibleCells, expectedVisibleCells ) << "Number of visible cells should match IJK range volume";
|
|
|
|
// Test case 2: Verify specific cells within bounds are visible
|
|
for ( size_t i = minIjk.x(); i <= maxIjk.x(); ++i )
|
|
{
|
|
for ( size_t j = minIjk.y(); j <= maxIjk.y(); ++j )
|
|
{
|
|
for ( size_t k = minIjk.z(); k <= maxIjk.z(); ++k )
|
|
{
|
|
size_t cellIndex = eclipseCase->mainGrid()->cellIndexFromIJK( i, j, k );
|
|
ASSERT_TRUE( visibility->val( cellIndex ) ) << "Cell at IJK (" << i << "," << j << "," << k << ") should be visible";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test case 3: Verify cells outside bounds are invisible
|
|
for ( size_t i = 0; i < std::min( minIjk.x(), size_t( 3 ) ); ++i )
|
|
{
|
|
for ( size_t j = 0; j < std::min( minIjk.y(), size_t( 3 ) ); ++j )
|
|
{
|
|
for ( size_t k = 0; k < std::min( minIjk.z(), size_t( 3 ) ); ++k )
|
|
{
|
|
size_t cellIndex = eclipseCase->mainGrid()->cellIndexFromIJK( i, j, k );
|
|
ASSERT_FALSE( visibility->val( cellIndex ) ) << "Cell at IJK (" << i << "," << j << "," << k << ") should be invisible";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test case 4: Edge case - single cell bounds
|
|
caf::VecIjk0 singleCellMin( 2, 2, 2 );
|
|
caf::VecIjk0 singleCellMax( 2, 2, 2 );
|
|
|
|
auto singleCellVisibility = RigEclipseCaseDataTools::createVisibilityFromIjkBounds( eclipseCase.p(), singleCellMin, singleCellMax );
|
|
|
|
ASSERT_FALSE( singleCellVisibility.isNull() ) << "Single cell visibility should not be null";
|
|
|
|
size_t singleCellVisibleCount = 0;
|
|
for ( size_t i = 0; i < totalCellCount; ++i )
|
|
{
|
|
if ( singleCellVisibility->val( i ) )
|
|
{
|
|
singleCellVisibleCount++;
|
|
}
|
|
}
|
|
|
|
ASSERT_EQ( singleCellVisibleCount, 1 ) << "Exactly one cell should be visible for single cell bounds";
|
|
|
|
// Verify the correct single cell is visible
|
|
size_t expectedSingleCellIndex = eclipseCase->mainGrid()->cellIndexFromIJK( 2, 2, 2 );
|
|
ASSERT_TRUE( singleCellVisibility->val( expectedSingleCellIndex ) ) << "The specified single cell should be visible";
|
|
|
|
// Test case 5: Error handling - invalid inputs
|
|
auto invalidVisibility1 = RigEclipseCaseDataTools::createVisibilityFromIjkBounds( nullptr, minIjk, maxIjk );
|
|
ASSERT_TRUE( invalidVisibility1.isNull() ) << "Null case data should return null visibility";
|
|
|
|
auto invalidVisibility2 = RigEclipseCaseDataTools::createVisibilityFromIjkBounds( eclipseCase.p(), caf::VecIjk0::UNDEFINED, maxIjk );
|
|
ASSERT_TRUE( invalidVisibility2.isNull() ) << "Undefined minIjk should return null visibility";
|
|
|
|
auto invalidVisibility3 = RigEclipseCaseDataTools::createVisibilityFromIjkBounds( eclipseCase.p(), minIjk, caf::VecIjk0::UNDEFINED );
|
|
ASSERT_TRUE( invalidVisibility3.isNull() ) << "Undefined maxIjk should return null visibility";
|
|
|
|
// Test case 6: Boundary conditions - bounds at grid limits
|
|
caf::VecIjk0 gridMinBounds( 0, 0, 0 );
|
|
caf::VecIjk0 gridMaxBounds( gridCellCountI - 1, gridCellCountJ - 1, gridCellCountK - 1 );
|
|
|
|
auto fullGridVisibility = RigEclipseCaseDataTools::createVisibilityFromIjkBounds( eclipseCase.p(), gridMinBounds, gridMaxBounds );
|
|
|
|
ASSERT_FALSE( fullGridVisibility.isNull() ) << "Full grid visibility should not be null";
|
|
|
|
size_t fullGridVisibleCount = 0;
|
|
for ( size_t i = 0; i < totalCellCount; ++i )
|
|
{
|
|
if ( fullGridVisibility->val( i ) )
|
|
{
|
|
fullGridVisibleCount++;
|
|
}
|
|
}
|
|
|
|
ASSERT_EQ( fullGridVisibleCount, totalCellCount ) << "All cells should be visible when using full grid bounds";
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
TEST( RigEclipseCaseDataToolsTest, GenerateBorderResultFromIjkBounds )
|
|
{
|
|
// Load the BRUGGE test model using RifReaderEclipseOutput
|
|
QDir baseFolder( TEST_MODEL_DIR );
|
|
bool subFolderExists = baseFolder.cd( "Case_with_10_timesteps/Real0" );
|
|
ASSERT_TRUE( subFolderExists ) << "Could not find test model directory";
|
|
|
|
QString filename( "BRUGGE_0000.EGRID" );
|
|
QString filePath = baseFolder.absoluteFilePath( filename );
|
|
ASSERT_TRUE( QFile::exists( filePath ) ) << "BRUGGE test model file does not exist: " << filePath.toStdString();
|
|
|
|
std::unique_ptr<RimEclipseResultCase> resultCase( new RimEclipseResultCase );
|
|
cvf::ref<RigEclipseCaseData> eclipseCase = new RigEclipseCaseData( resultCase.get() );
|
|
|
|
cvf::ref<RifReaderEclipseOutput> readerInterfaceEcl = new RifReaderEclipseOutput;
|
|
bool success = readerInterfaceEcl->open( filePath, eclipseCase.p() );
|
|
ASSERT_TRUE( success ) << "Could not load BRUGGE test model";
|
|
|
|
resultCase->setReservoirData( eclipseCase.p() );
|
|
|
|
// Define IJK bounds for testing
|
|
caf::VecIjk0 minIjk( 20, 20, 5 );
|
|
caf::VecIjk0 maxIjk( 30, 30, 8 );
|
|
|
|
// Create visibility from IJK bounds
|
|
auto visibility = RigEclipseCaseDataTools::createVisibilityFromIjkBounds( eclipseCase.p(), minIjk, maxIjk );
|
|
ASSERT_FALSE( visibility.isNull() ) << "Visibility should be created successfully";
|
|
|
|
// Generate border result using refined methods (no refinement: 1,1,1)
|
|
RigNoRefinement refinement( cvf::Vec3st( maxIjk.i() - minIjk.i() + 1, maxIjk.j() - minIjk.j() + 1, maxIjk.k() - minIjk.k() + 1 ) );
|
|
RigGridExportAdapter gridAdapter( eclipseCase.p(), minIjk, maxIjk, refinement, visibility.p() );
|
|
|
|
// Create refined visibility
|
|
std::vector<int> refinedVisibility = RigSimulationInputTool::createRefinedVisibility( gridAdapter );
|
|
|
|
auto refinedBorderResult = RigEclipseResultTools::generateBorderResult( gridAdapter, refinedVisibility );
|
|
|
|
// Map refined border result back to original grid (since refinement=1, it's 1:1 but with different indexing)
|
|
auto mainGrid = eclipseCase->mainGrid();
|
|
size_t reservoirCellCount = eclipseCase->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->reservoirCellCount();
|
|
std::vector<int> borderResult( reservoirCellCount, RigEclipseResultTools::BorderType::INVISIBLE_CELL );
|
|
|
|
for ( size_t rk = 0; rk < gridAdapter.cellCountK(); ++rk )
|
|
{
|
|
for ( size_t rj = 0; rj < gridAdapter.cellCountJ(); ++rj )
|
|
{
|
|
for ( size_t ri = 0; ri < gridAdapter.cellCountI(); ++ri )
|
|
{
|
|
size_t refinedIdx = rk * gridAdapter.cellCountI() * gridAdapter.cellCountJ() + rj * gridAdapter.cellCountI() + ri;
|
|
size_t origI = minIjk.x() + ri;
|
|
size_t origJ = minIjk.y() + rj;
|
|
size_t origK = minIjk.z() + rk;
|
|
size_t origCellIdx = mainGrid->cellIndexFromIJK( origI, origJ, origK );
|
|
borderResult[origCellIdx] = refinedBorderResult[refinedIdx];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that the result was generated
|
|
ASSERT_GT( borderResult.size(), 0 ) << "Border result should not be empty";
|
|
|
|
// Count cells with different values
|
|
int invisibleCells = 0;
|
|
int borderCells = 0;
|
|
int interiorCells = 0;
|
|
|
|
for ( int value : borderResult )
|
|
{
|
|
if ( value == RigEclipseResultTools::BorderType::INVISIBLE_CELL )
|
|
invisibleCells++;
|
|
else if ( value == RigEclipseResultTools::BorderType::BORDER_CELL )
|
|
borderCells++;
|
|
else if ( value == RigEclipseResultTools::BorderType::INTERIOR_CELL )
|
|
interiorCells++;
|
|
}
|
|
|
|
// Verify that we have all three types of cells
|
|
ASSERT_GT( invisibleCells, 0 ) << "Should have invisible cells (value 0)";
|
|
ASSERT_GT( borderCells, 0 ) << "Should have border cells (value 1)";
|
|
ASSERT_GE( interiorCells, 0 ) << "May have interior cells (value 2)";
|
|
|
|
// The total should match the result vector size
|
|
int totalCells = invisibleCells + borderCells + interiorCells;
|
|
ASSERT_EQ( totalCells, static_cast<int>( borderResult.size() ) ) << "Total should match result vector size";
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
TEST( RigEclipseCaseDataToolsTest, GenerateOperNumResultFromBorderResult )
|
|
{
|
|
// Load the BRUGGE test model using RifReaderEclipseOutput
|
|
QDir baseFolder( TEST_MODEL_DIR );
|
|
bool subFolderExists = baseFolder.cd( "Case_with_10_timesteps/Real0" );
|
|
ASSERT_TRUE( subFolderExists ) << "Could not find test model directory";
|
|
|
|
QString filename( "BRUGGE_0000.EGRID" );
|
|
QString filePath = baseFolder.absoluteFilePath( filename );
|
|
ASSERT_TRUE( QFile::exists( filePath ) ) << "BRUGGE test model file does not exist: " << filePath.toStdString();
|
|
|
|
std::unique_ptr<RimEclipseResultCase> resultCase( new RimEclipseResultCase );
|
|
cvf::ref<RigEclipseCaseData> eclipseCase = new RigEclipseCaseData( resultCase.get() );
|
|
|
|
cvf::ref<RifReaderEclipseOutput> readerInterfaceEcl = new RifReaderEclipseOutput;
|
|
bool success = readerInterfaceEcl->open( filePath, eclipseCase.p() );
|
|
ASSERT_TRUE( success ) << "Could not load BRUGGE test model";
|
|
|
|
resultCase->setReservoirData( eclipseCase.p() );
|
|
|
|
// Define IJK bounds for testing
|
|
caf::VecIjk0 minIjk( 20, 20, 5 );
|
|
caf::VecIjk0 maxIjk( 30, 30, 8 );
|
|
|
|
// Create visibility from IJK bounds
|
|
auto visibility = RigEclipseCaseDataTools::createVisibilityFromIjkBounds( eclipseCase.p(), minIjk, maxIjk );
|
|
ASSERT_FALSE( visibility.isNull() ) << "Visibility should be created successfully";
|
|
|
|
// Generate border result using refined methods (no refinement: 1,1,1)
|
|
RigNoRefinement refinement( cvf::Vec3st( maxIjk.i() - minIjk.i() + 1, maxIjk.j() - minIjk.j() + 1, maxIjk.k() - minIjk.k() + 1 ) );
|
|
RigGridExportAdapter gridAdapter( eclipseCase.p(), minIjk, maxIjk, refinement, visibility.p() );
|
|
|
|
// Create refined visibility
|
|
std::vector<int> refinedVisibility = RigSimulationInputTool::createRefinedVisibility( gridAdapter );
|
|
|
|
auto refinedBorderResult = RigEclipseResultTools::generateBorderResult( gridAdapter, refinedVisibility );
|
|
|
|
// Map refined border result back to original grid (since refinement=1, it's 1:1 but with different indexing)
|
|
auto mainGrid = eclipseCase->mainGrid();
|
|
size_t reservoirCellCount = eclipseCase->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->reservoirCellCount();
|
|
std::vector<int> borderResult( reservoirCellCount, RigEclipseResultTools::BorderType::INVISIBLE_CELL );
|
|
|
|
for ( size_t rk = 0; rk < gridAdapter.cellCountK(); ++rk )
|
|
{
|
|
for ( size_t rj = 0; rj < gridAdapter.cellCountJ(); ++rj )
|
|
{
|
|
for ( size_t ri = 0; ri < gridAdapter.cellCountI(); ++ri )
|
|
{
|
|
size_t refinedIdx = rk * gridAdapter.cellCountI() * gridAdapter.cellCountJ() + rj * gridAdapter.cellCountI() + ri;
|
|
size_t origI = minIjk.x() + ri;
|
|
size_t origJ = minIjk.y() + rj;
|
|
size_t origK = minIjk.z() + rk;
|
|
size_t origCellIdx = mainGrid->cellIndexFromIJK( origI, origJ, origK );
|
|
borderResult[origCellIdx] = refinedBorderResult[refinedIdx];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify border result was generated
|
|
ASSERT_GT( borderResult.size(), 0 ) << "Border result should not be empty";
|
|
|
|
// Test case 1: Generate OPERNUM with automatic border cell value (no existing OPERNUM, should get 2)
|
|
// Use refined border result since gridAdapter was created with refinement=1
|
|
int maxOperNum = RigEclipseResultTools::findMaxOperNumValue( resultCase.get() );
|
|
auto [opernumResult, operNumRegion] =
|
|
RigEclipseResultTools::generateOperNumResult( resultCase.get(), gridAdapter, refinedBorderResult, maxOperNum );
|
|
|
|
// Verify OPERNUM was generated
|
|
ASSERT_EQ( refinedBorderResult.size(), opernumResult.size() ) << "Refined BORDNUM and OPERNUM should have same size";
|
|
ASSERT_GT( opernumResult.size(), 0 ) << "OPERNUM vector should not be empty";
|
|
ASSERT_EQ( operNumRegion, 2 ) << "operNumRegion should be 2 (no existing OPERNUM, default value)";
|
|
|
|
// Count cells with different values and verify the logic
|
|
int borderCellsWithOperValue = 0; // BORDNUM=1 cells that should get OPERNUM=2 (max 0 + 1, then default 2)
|
|
int defaultOperCells = 0; // BORDNUM!=1 cells that should get OPERNUM=1
|
|
int borderCellsTotal = 0; // Total BORDNUM=1 cells
|
|
|
|
for ( size_t i = 0; i < refinedBorderResult.size(); ++i )
|
|
{
|
|
int bordnumValue = refinedBorderResult[i];
|
|
int opernumValue = opernumResult[i];
|
|
|
|
if ( bordnumValue == RigEclipseResultTools::BorderType::BORDER_CELL )
|
|
{
|
|
borderCellsTotal++;
|
|
ASSERT_EQ( opernumValue, 2 ) << "Border cells (BORDNUM=1) should have OPERNUM=2 at index " << i;
|
|
borderCellsWithOperValue++;
|
|
}
|
|
else
|
|
{
|
|
ASSERT_EQ( opernumValue, 1 ) << "Non-border cells should have OPERNUM=1 at index " << i;
|
|
defaultOperCells++;
|
|
}
|
|
}
|
|
|
|
// Verify we have the expected distribution
|
|
ASSERT_GT( borderCellsTotal, 0 ) << "Should have border cells (BORDNUM=1)";
|
|
ASSERT_EQ( borderCellsWithOperValue, borderCellsTotal ) << "All border cells should get OPERNUM=2";
|
|
ASSERT_GT( defaultOperCells, 0 ) << "Should have non-border cells with OPERNUM=1";
|
|
|
|
// Test case 2: Test automatic max value detection with existing OPERNUM
|
|
// First create a custom OPERNUM result with some predefined values
|
|
auto activeReservoirCellIdxs = eclipseCase->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices();
|
|
int numActiveCells = static_cast<int>( activeReservoirCellIdxs.size() );
|
|
|
|
std::vector<int> customOperValues;
|
|
customOperValues.resize( eclipseCase->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->reservoirCellCount(),
|
|
5 ); // Set all to 5 initially
|
|
// Add some higher values to test max detection
|
|
if ( numActiveCells > 10 )
|
|
{
|
|
customOperValues[5] = 15; // Maximum will be 15
|
|
customOperValues[8] = 12;
|
|
}
|
|
|
|
// Create the OPERNUM result directly
|
|
RigEclipseResultTools::createResultVector( *resultCase, RiaResultNames::opernum(), customOperValues );
|
|
|
|
// Test case 3: Test the utility function directly (before regeneration)
|
|
int maxOperValue = RigEclipseResultTools::findMaxOperNumValue( resultCase.get() );
|
|
ASSERT_EQ( maxOperValue, 15 ) << "Max OPERNUM value should be 15";
|
|
|
|
// Now generate OPERNUM again - should use max value + 1 = 16 for border cells
|
|
// This should use the existing OPERNUM data we just created and refine it
|
|
auto [updatedOpernumResult, updatedOperNumRegion] =
|
|
RigEclipseResultTools::generateOperNumResult( resultCase.get(), gridAdapter, refinedBorderResult, maxOperValue );
|
|
|
|
// Verify the updated operNumRegion is correct
|
|
ASSERT_EQ( updatedOperNumRegion, 16 ) << "updatedOperNumRegion should be 16 (max 15 + 1)";
|
|
|
|
// Verify the behavior - border cells should be 16, non-border cells should preserve existing OPERNUM values (5, 12, or 15)
|
|
int borderCells = 0;
|
|
int nonBorderCells = 0;
|
|
int cellsWithValue5 = 0, cellsWithValue12 = 0, cellsWithValue15 = 0;
|
|
|
|
for ( size_t i = 0; i < refinedBorderResult.size(); ++i )
|
|
{
|
|
int bordnumValue = refinedBorderResult[i];
|
|
int opernumValue = updatedOpernumResult[i];
|
|
|
|
if ( bordnumValue == RigEclipseResultTools::BorderType::BORDER_CELL )
|
|
{
|
|
ASSERT_EQ( opernumValue, 16 ) << "Border cells should be updated to OPERNUM=16 (max 15 + 1) at index " << i;
|
|
borderCells++;
|
|
}
|
|
else
|
|
{
|
|
// Non-border cells should preserve the refined OPERNUM values from the original grid (5, 12, or 15)
|
|
ASSERT_TRUE( opernumValue == 5 || opernumValue == 12 || opernumValue == 15 )
|
|
<< "Non-border cells should have refined OPERNUM value (5, 12, or 15) at index " << i << ", got " << opernumValue;
|
|
nonBorderCells++;
|
|
|
|
if ( opernumValue == 5 )
|
|
cellsWithValue5++;
|
|
else if ( opernumValue == 12 )
|
|
cellsWithValue12++;
|
|
else if ( opernumValue == 15 )
|
|
cellsWithValue15++;
|
|
}
|
|
}
|
|
|
|
ASSERT_GT( borderCells, 0 ) << "Should have some border cells";
|
|
ASSERT_GT( nonBorderCells, 0 ) << "Should have some non-border cells";
|
|
ASSERT_GT( cellsWithValue5, 0 ) << "Should have cells with OPERNUM=5 (the default value we set)";
|
|
|
|
// Total should match vector size
|
|
int totalChecked = borderCells + nonBorderCells;
|
|
ASSERT_EQ( totalChecked, static_cast<int>( updatedOpernumResult.size() ) ) << "Should check all cells";
|
|
}
|