Simulation Input: Refactor border conditions to use in-memory results.

This commit is contained in:
Kristian Bendiksen committed 2025-12-12 13:38:22 +01:00
1 parent 55a4863436
commit cfcdd67255
6 files changed
+241 -261

No files matched your search

@@ -107,9 +107,9 @@ void createResultVector( RimEclipseCase& eclipseCase, const QString& resultName,
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// ///
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
void generateBorderResult( RimEclipseCase* eclipseCase, cvf::ref<cvf::UByteArray> customVisibility, const QString& resultName ) std::vector<int> generateBorderResult( RimEclipseCase* eclipseCase, cvf::ref<cvf::UByteArray> customVisibility )
{ {
if ( eclipseCase == nullptr || customVisibility.isNull() ) return; if ( eclipseCase == nullptr || customVisibility.isNull() ) return {};
auto activeReservoirCellIdxs = auto activeReservoirCellIdxs =
eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices(); eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices();
@@ -147,17 +147,15 @@ void generateBorderResult( RimEclipseCase* eclipseCase, cvf::ref<cvf::UByteArray
} }
} }
RigEclipseResultTools::createResultVector( *eclipseCase, resultName, result ); return result;
eclipseCase->updateConnectedEditors();
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// ///
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
int generateOperNumResult( RimEclipseCase* eclipseCase, int borderCellValue ) std::vector<int> generateOperNumResult( RimEclipseCase* eclipseCase, const std::vector<int>& borderResult, int borderCellValue )
{ {
if ( eclipseCase == nullptr ) return 0; if ( eclipseCase == nullptr ) return {};
// Auto-determine border cell value if not specified // Auto-determine border cell value if not specified
if ( borderCellValue == -1 ) if ( borderCellValue == -1 )
@@ -211,32 +209,21 @@ int generateOperNumResult( RimEclipseCase* eclipseCase, int borderCellValue )
borderCellValue = 2; borderCellValue = 2;
} }
// Check if BORDNUM exists to modify border cells if ( !borderResult.empty() )
RigEclipseResultAddress bordNumAddr( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, RiaResultNames::bordnum() );
if ( resultsData->hasResultEntry( bordNumAddr ) )
{ {
resultsData->ensureKnownResultLoaded( bordNumAddr ); result.resize( borderResult.size(), 1 );
auto bordNumValues = resultsData->cellScalarResults( bordNumAddr, 0 );
if ( !bordNumValues.empty() )
{
result.resize( bordNumValues.size(), 1 );
for ( auto activeCellIdx : activeReservoirCellIdxs ) for ( auto activeCellIdx : activeReservoirCellIdxs )
{
// If BORDNUM = 1 (BORDER_CELL), assign the border cell value
if ( borderResult[activeCellIdx.value()] == BorderType::BORDER_CELL )
{ {
// If BORDNUM = 1 (BORDER_CELL), assign the border cell value result[activeCellIdx.value()] = borderCellValue;
if ( static_cast<int>( bordNumValues[activeCellIdx.value()] ) == BorderType::BORDER_CELL )
{
result[activeCellIdx.value()] = borderCellValue;
}
} }
} }
} }
RigEclipseResultTools::createResultVector( *eclipseCase, RiaResultNames::opernum(), result ); return result;
eclipseCase->updateConnectedEditors();
return borderCellValue;
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@@ -262,28 +249,22 @@ int findMaxBcconValue( RimEclipseCase* eclipseCase )
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// ///
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
void generateBcconResult( RimEclipseCase* eclipseCase, const caf::VecIjk0& min, const caf::VecIjk0& max ) std::vector<int> generateBcconResult( RimEclipseCase* eclipseCase,
const std::vector<int>& borderResult,
const caf::VecIjk0& min,
const caf::VecIjk0& max )
{ {
if ( eclipseCase == nullptr ) return; if ( eclipseCase == nullptr ) return {};
auto resultsData = eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
if ( !resultsData ) return;
auto grid = eclipseCase->eclipseCaseData()->mainGrid(); auto grid = eclipseCase->eclipseCaseData()->mainGrid();
if ( !grid ) return; if ( !grid ) return {};
// Check if BORDNUM result exists if ( borderResult.empty() )
RigEclipseResultAddress bordNumAddr( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, RiaResultNames::bordnum() );
if ( !resultsData->hasResultEntry( bordNumAddr ) )
{ {
RiaLogging::warning( "BORDNUM result not found - cannot generate BCCON result" ); RiaLogging::warning( "Border result is empty - cannot generate BCCON result" );
return; return {};
} }
resultsData->ensureKnownResultLoaded( bordNumAddr );
auto bordNumValues = resultsData->cellScalarResults( bordNumAddr, 0 );
if ( bordNumValues.empty() ) return;
auto activeReservoirCellIdxs = auto activeReservoirCellIdxs =
eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices(); eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices();
@@ -295,7 +276,7 @@ void generateBcconResult( RimEclipseCase* eclipseCase, const caf::VecIjk0& min,
for ( auto activeCellIdx : activeReservoirCellIdxs ) for ( auto activeCellIdx : activeReservoirCellIdxs )
{ {
// Check if this cell is a border cell // Check if this cell is a border cell
int borderValue = static_cast<int>( bordNumValues[activeCellIdx.value()] ); int borderValue = borderResult[activeCellIdx.value()];
if ( borderValue != BorderType::BORDER_CELL ) continue; if ( borderValue != BorderType::BORDER_CELL ) continue;
// Get IJK indices for this cell // Get IJK indices for this cell
@@ -334,38 +315,22 @@ void generateBcconResult( RimEclipseCase* eclipseCase, const caf::VecIjk0& min,
result[activeCellIdx.value()] = bcconValue; result[activeCellIdx.value()] = bcconValue;
} }
RigEclipseResultTools::createResultVector( *eclipseCase, "BCCON", result ); return result;
eclipseCase->updateConnectedEditors();
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// ///
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
std::vector<BorderCellFace> generateBorderCellFaces( RimEclipseCase* eclipseCase ) std::vector<BorderCellFace> generateBorderCellFaces( RimEclipseCase* eclipseCase,
const std::vector<int>& borderResult,
const std::vector<int>& bcconResult )
{ {
if ( eclipseCase == nullptr ) return {}; if ( eclipseCase == nullptr ) return {};
auto resultsData = eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
if ( !resultsData ) return {};
auto grid = eclipseCase->eclipseCaseData()->mainGrid(); auto grid = eclipseCase->eclipseCaseData()->mainGrid();
if ( !grid ) return {}; if ( !grid ) return {};
// Check if BORDNUM result exists if ( borderResult.empty() || bcconResult.empty() ) return {};
RigEclipseResultAddress bordNumAddr( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, RiaResultNames::bordnum() );
if ( !resultsData->hasResultEntry( bordNumAddr ) ) return {}; // BORDNUM not generated yet
resultsData->ensureKnownResultLoaded( bordNumAddr );
auto bordNumValues = resultsData->cellScalarResults( bordNumAddr, 0 );
if ( bordNumValues.empty() ) return {};
// Check if BCCON result exists to get boundary condition values
RigEclipseResultAddress bcconAddr( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, "BCCON" );
if ( !resultsData->hasResultEntry( bcconAddr ) ) return {};
resultsData->ensureKnownResultLoaded( bcconAddr );
std::vector<double> bcconValues = resultsData->cellScalarResults( bcconAddr, 0 );
auto activeReservoirCellIdxs = auto activeReservoirCellIdxs =
eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices(); eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices();
@@ -376,7 +341,7 @@ std::vector<BorderCellFace> generateBorderCellFaces( RimEclipseCase* eclipseCase
for ( auto activeCellIdx : activeReservoirCellIdxs ) for ( auto activeCellIdx : activeReservoirCellIdxs )
{ {
// Check if this cell is a border cell // Check if this cell is a border cell
int borderValue = static_cast<int>( bordNumValues[activeCellIdx.value()] ); int borderValue = borderResult[activeCellIdx.value()];
if ( borderValue != BorderType::BORDER_CELL ) continue; if ( borderValue != BorderType::BORDER_CELL ) continue;
// Get IJK indices for this cell // Get IJK indices for this cell
@@ -403,11 +368,11 @@ std::vector<BorderCellFace> generateBorderCellFaces( RimEclipseCase* eclipseCase
if ( it == activeReservoirCellIdxs.end() ) continue; // Neighbor not active if ( it == activeReservoirCellIdxs.end() ) continue; // Neighbor not active
// Check if neighbor is an interior cell // Check if neighbor is an interior cell
int neighborBorderValue = static_cast<int>( bordNumValues[neighborReservoirIdx] ); int neighborBorderValue = borderResult[neighborReservoirIdx];
if ( neighborBorderValue == BorderType::INTERIOR_CELL ) if ( neighborBorderValue == BorderType::INTERIOR_CELL )
{ {
// Get boundary condition value from BCCON grid property // Get boundary condition value from BCCON grid property
int boundaryCondition = static_cast<int>( bcconValues[activeCellIdx.value()] ); int boundaryCondition = bcconResult[activeCellIdx.value()];
if ( boundaryCondition > 0 ) if ( boundaryCondition > 0 )
{ {
// Add this face to the result // Add this face to the result
@@ -46,16 +46,21 @@ struct BorderCellFace
void createResultVector( RimEclipseCase& eclipseCase, const QString& resultName, const std::vector<int>& intValues ); void createResultVector( RimEclipseCase& eclipseCase, const QString& resultName, const std::vector<int>& intValues );
void generateBorderResult( RimEclipseCase* eclipseCase, cvf::ref<cvf::UByteArray> customVisibility, const QString& resultName = "BORDER" ); std::vector<int> generateBorderResult( RimEclipseCase* eclipseCase, cvf::ref<cvf::UByteArray> customVisibility );
int generateOperNumResult( RimEclipseCase* eclipseCase, int borderCellValue = -1 ); std::vector<int> generateOperNumResult( RimEclipseCase* eclipseCase, const std::vector<int>& borderResult, int borderCellValue = -1 );
int findMaxOperNumValue( RimEclipseCase* eclipseCase ); int findMaxOperNumValue( RimEclipseCase* eclipseCase );
int findMaxBcconValue( RimEclipseCase* eclipseCase ); int findMaxBcconValue( RimEclipseCase* eclipseCase );
void generateBcconResult( RimEclipseCase* eclipseCase, const caf::VecIjk0& min, const caf::VecIjk0& max ); std::vector<int> generateBcconResult( RimEclipseCase* eclipseCase,
const std::vector<int>& borderResult,
const caf::VecIjk0& min,
const caf::VecIjk0& max );
std::vector<BorderCellFace> generateBorderCellFaces( RimEclipseCase* eclipseCase ); std::vector<BorderCellFace> generateBorderCellFaces( RimEclipseCase* eclipseCase,
const std::vector<int>& borderResult,
const std::vector<int>& bcconResult );
} // namespace RigEclipseResultTools } // namespace RigEclipseResultTools
@@ -20,9 +20,6 @@
#include "RiaLogging.h" #include "RiaLogging.h"
#include "RiaResultNames.h"
#include "RigSimulationInputSettings.h"
#include "RifEclipseInputFileTools.h" #include "RifEclipseInputFileTools.h"
#include "RifOpmDeckTools.h" #include "RifOpmDeckTools.h"
#include "RifOpmFlowDeckFile.h" #include "RifOpmFlowDeckFile.h"
@@ -35,6 +32,7 @@
#include "RigEclipseResultTools.h" #include "RigEclipseResultTools.h"
#include "RigGridExportAdapter.h" #include "RigGridExportAdapter.h"
#include "RigResdataGridConverter.h" #include "RigResdataGridConverter.h"
#include "RigSimulationInputSettings.h"
#include "Well/RigSimWellData.h" #include "Well/RigSimWellData.h"
#include "Well/RigWellResultFrame.h" #include "Well/RigWellResultFrame.h"
#include "Well/RigWellResultPoint.h" #include "Well/RigWellResultPoint.h"
@@ -82,28 +80,6 @@ std::expected<void, QString> RigSimulationInputTool::exportSimulationInput( RimE
return result; return result;
} }
// Generate BORDNUM result based on visibility
if ( visibility && visibility->size() > 0 )
{
RigEclipseResultTools::generateBorderResult( &eclipseCase, visibility, RiaResultNames::bordnum() );
if ( settings.boundaryCondition() == RiaModelExportDefines::BoundaryCondition::BCCON_BCPROP )
{
// Generate BCCON result to assign values 1-6 based on which face of the box the border cells are on
RigEclipseResultTools::generateBcconResult( &eclipseCase, settings.min(), settings.max() );
}
else if ( settings.boundaryCondition() == RiaModelExportDefines::BoundaryCondition::OPERNUM_OPERATER )
{
// Generate OPERNUM result based on BORDNUM (border cells get max existing OPERNUM + 1)
int operNumRegion = RigEclipseResultTools::generateOperNumResult( &eclipseCase );
if ( auto result = addOperNumRegionAndOperater( &eclipseCase, settings, deckFile, operNumRegion, settings.porvMultiplier() );
!result )
{
return result;
}
}
}
if ( auto result = replaceKeywordValuesInDeckFile( &eclipseCase, settings, deckFile ); !result ) if ( auto result = replaceKeywordValuesInDeckFile( &eclipseCase, settings, deckFile ); !result )
{ {
return result; return result;
@@ -144,7 +120,7 @@ std::expected<void, QString> RigSimulationInputTool::exportSimulationInput( RimE
return result; return result;
} }
if ( auto result = addBorderBoundaryConditions( &eclipseCase, settings, deckFile ); !result ) if ( auto result = addBorderBoundaryConditions( &eclipseCase, settings, visibility, deckFile ); !result )
{ {
return result; return result;
} }
@@ -311,59 +287,115 @@ std::expected<void, QString> RigSimulationInputTool::replaceKeywordValuesInDeckF
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
std::expected<void, QString> RigSimulationInputTool::addBorderBoundaryConditions( RimEclipseCase* eclipseCase, std::expected<void, QString> RigSimulationInputTool::addBorderBoundaryConditions( RimEclipseCase* eclipseCase,
const RigSimulationInputSettings& settings, const RigSimulationInputSettings& settings,
cvf::ref<cvf::UByteArray> visibility,
RifOpmFlowDeckFile& deckFile ) RifOpmFlowDeckFile& deckFile )
{ {
// Generate border cell faces // Return early if no visibility
auto borderCellFaces = RigEclipseResultTools::generateBorderCellFaces( eclipseCase ); if ( visibility.isNull() || visibility->size() == 0 )
if ( !borderCellFaces.empty() )
{ {
// Transform border cell face coordinates to sector-relative coordinates RiaLogging::info( "No visibility provided - skipping boundary conditions" );
for ( auto& face : borderCellFaces ) return {};
{ }
auto transformResult =
RigGridExportAdapter::transformIjkToSectorCoordinates( face.ijk, settings.min(), settings.max(), settings.refinement(), true );
if ( !transformResult ) // Generate border result (BORDNUM) in memory
auto borderResult = RigEclipseResultTools::generateBorderResult( eclipseCase, visibility );
if ( borderResult.empty() )
{
RiaLogging::warning( "Failed to generate border result - skipping boundary conditions" );
return {};
}
// Handle boundary condition based on settings
if ( settings.boundaryCondition() == RiaModelExportDefines::BoundaryCondition::BCCON_BCPROP )
{
// Generate BCCON result in memory
auto bcconResult = RigEclipseResultTools::generateBcconResult( eclipseCase, borderResult, settings.min(), settings.max() );
if ( bcconResult.empty() )
{
RiaLogging::warning( "Failed to generate BCCON result - skipping boundary conditions" );
return {};
}
// Generate border cell faces using in-memory results
auto borderCellFaces = RigEclipseResultTools::generateBorderCellFaces( eclipseCase, borderResult, bcconResult );
if ( !borderCellFaces.empty() )
{
// Transform border cell face coordinates to sector-relative coordinates
for ( auto& face : borderCellFaces )
{ {
RiaLogging::warning( QString( "Failed to transform border cell face at (%1, %2, %3): %4" ) auto transformResult =
.arg( face.ijk.x() ) RigGridExportAdapter::transformIjkToSectorCoordinates( face.ijk, settings.min(), settings.max(), settings.refinement(), true );
.arg( face.ijk.y() )
.arg( face.ijk.z() ) if ( !transformResult )
.arg( transformResult.error() ) ); {
continue; RiaLogging::warning( QString( "Failed to transform border cell face at (%1, %2, %3): %4" )
.arg( face.ijk.x() )
.arg( face.ijk.y() )
.arg( face.ijk.z() )
.arg( transformResult.error() ) );
continue;
}
// Update the IJK coordinates to sector-relative (1-based Eclipse coordinates)
// Note: RigGridExportAdapter::transformIjkToSectorCoordinates returns 1-based coordinates, but we need to convert back to
// 0-based for the BorderCellFace struct
face.ijk = transformResult->toZeroBased();
} }
// Update the IJK coordinates to sector-relative (1-based Eclipse coordinates) // Create BCCON keyword using the factory
// Note: RigGridExportAdapter::transformIjkToSectorCoordinates returns 1-based coordinates, but we need to convert back to Opm::DeckKeyword bcconKw = RimKeywordFactory::bcconKeyword( borderCellFaces );
// 0-based for the BorderCellFace struct
face.ijk = transformResult->toZeroBased(); // Replace BCCON keyword in GRID section
if ( !deckFile.replaceKeyword( "GRID", bcconKw ) )
{
return std::unexpected( "Failed to replace BCCON keyword in deck file" );
}
// Build BCPROP records from the settings configuration
std::vector<Opm::DeckRecord> bcpropRecords = settings.bcpropKeywords();
// Create BCPROP keyword using the factory
Opm::DeckKeyword bcpropKw = RimKeywordFactory::bcpropKeyword( borderCellFaces, bcpropRecords );
// Replace BCPROP keyword in GRID section
if ( !deckFile.replaceKeyword( Opm::ParserKeywords::SCHEDULE::keywordName, bcpropKw ) )
{
return std::unexpected( "Failed to replace BCPROP keyword in deck file" );
}
} }
else
// Create BCCON keyword using the factory
Opm::DeckKeyword bcconKw = RimKeywordFactory::bcconKeyword( borderCellFaces );
// Replace BCCON keyword in GRID section
if ( !deckFile.replaceKeyword( "GRID", bcconKw ) )
{ {
return std::unexpected( "Failed to replace BCCON keyword in deck file" ); RiaLogging::warning( "No border cells found - skipping BCCON/BCPROP keyword generation" );
}
// Build BCPROP records from the settings configuration
std::vector<Opm::DeckRecord> bcpropRecords = settings.bcpropKeywords();
// Create BCPROP keyword using the factory
Opm::DeckKeyword bcpropKw = RimKeywordFactory::bcpropKeyword( borderCellFaces, bcpropRecords );
// Replace BCPROP keyword in GRID section
if ( !deckFile.replaceKeyword( Opm::ParserKeywords::SCHEDULE::keywordName, bcpropKw ) )
{
return std::unexpected( "Failed to replace BCPROP keyword in deck file" );
} }
} }
else else if ( settings.boundaryCondition() == RiaModelExportDefines::BoundaryCondition::OPERNUM_OPERATER )
{ {
RiaLogging::warning( "No border cells found - skipping BCCON/BCPROP keyword generation" ); // Generate OPERNUM result based on border result (border cells get max existing OPERNUM + 1)
auto operNumResult = RigEclipseResultTools::generateOperNumResult( eclipseCase, borderResult );
// Find the border cell value used
int operNumRegion = -1;
for ( size_t i = 0; i < operNumResult.size(); i++ )
{
if ( borderResult[i] == RigEclipseResultTools::BorderType::BORDER_CELL )
{
operNumRegion = operNumResult[i];
break;
}
}
if ( operNumRegion != -1 )
{
if ( auto result =
addOperNumRegionAndOperater( eclipseCase, settings, deckFile, operNumResult, operNumRegion, settings.porvMultiplier() );
!result )
{
return result;
}
}
} }
return {}; return {};
@@ -1431,6 +1463,7 @@ std::expected<void, QString> RigSimulationInputTool::filterAndUpdateWellKeywords
std::expected<void, QString> RigSimulationInputTool::addOperNumRegionAndOperater( RimEclipseCase* eclipseCase, std::expected<void, QString> RigSimulationInputTool::addOperNumRegionAndOperater( RimEclipseCase* eclipseCase,
const RigSimulationInputSettings& settings, const RigSimulationInputSettings& settings,
RifOpmFlowDeckFile& deckFile, RifOpmFlowDeckFile& deckFile,
const std::vector<int>& operNumResult,
int operNumRegion, int operNumRegion,
double porvMultiplier ) double porvMultiplier )
{ {
@@ -1483,33 +1516,56 @@ std::expected<void, QString> RigSimulationInputTool::addOperNumRegionAndOperater
Opm::DeckKeyword newKw( ( Opm::ParserKeywords::OPERNUM() ) ); Opm::DeckKeyword newKw( ( Opm::ParserKeywords::OPERNUM() ) );
deckFile.addKeyword( "GRID", newKw ); deckFile.addKeyword( "GRID", newKw );
} }
}
// Try to extract keyword data // Export OPERNUM data for the sector (with refinement)
auto result = RifEclipseInputFileTools::extractKeywordData( eclipseCase->eclipseCaseData(), auto mainGrid = eclipseCase->eclipseCaseData()->mainGrid();
"OPERNUM", auto activeCells = eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
settings.min(), auto refinementV = settings.refinement();
settings.max(), auto min = settings.min();
settings.refinement() ); auto max = settings.max();
if ( result )
std::vector<double> exportedData;
cvf::Vec3st refinedMin( min.x() * refinementV.x(), min.y() * refinementV.y(), min.z() * refinementV.z() );
cvf::Vec3st refinedMax( ( max.x() + 1 ) * refinementV.x(), ( max.y() + 1 ) * refinementV.y(), ( max.z() + 1 ) * refinementV.z() );
for ( size_t k = refinedMin.z(); k < refinedMax.z(); ++k )
{
size_t mainK = k / refinementV.z();
for ( size_t j = refinedMin.y(); j < refinedMax.y(); ++j )
{ {
// Replace keyword values in deck with extracted data size_t mainJ = j / refinementV.y();
if ( deckFile.replaceKeywordData( "OPERNUM", result.value() ) ) for ( size_t i = refinedMin.x(); i < refinedMax.x(); ++i )
{ {
RiaLogging::info( "Added replaced opernum value in existing position." ); size_t mainI = i / refinementV.x();
} size_t reservoirCellIndex = mainGrid->cellIndexFromIJK( mainI, mainJ, mainK );
else size_t resIndex = activeCells->cellResultIndex( reservoirCellIndex );
{
return std::unexpected( "Unable to replace OPERNUM values" ); if ( resIndex != cvf::UNDEFINED_SIZE_T && reservoirCellIndex < operNumResult.size() )
{
exportedData.push_back( static_cast<double>( operNumResult[reservoirCellIndex] ) );
}
else
{
exportedData.push_back( 1.0 ); // Default value for inactive cells
}
} }
} }
else }
{
return std::unexpected( "No OPERNUM result found." ); if ( exportedData.empty() )
} {
return std::unexpected( "Failed to export OPERNUM data for sector" );
}
// Replace keyword values in deck with exported data
if ( deckFile.replaceKeywordData( "OPERNUM", exportedData ) )
{
RiaLogging::info( "Added replaced opernum value in existing position." );
} }
else else
{ {
RiaLogging::info( "OPERNUM keyword already exists in deck, skipping INCLUDE" ); return std::unexpected( "Unable to replace OPERNUM values" );
} }
// Create OPERATER keyword to multiply pore volume in border region // Create OPERATER keyword to multiply pore volume in border region
@@ -83,8 +83,10 @@ private:
static std::expected<void, QString> static std::expected<void, QString>
updateWelldimsKeyword( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile ); updateWelldimsKeyword( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );
static std::expected<void, QString> static std::expected<void, QString> addBorderBoundaryConditions( RimEclipseCase* eclipseCase,
addBorderBoundaryConditions( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile ); const RigSimulationInputSettings& settings,
cvf::ref<cvf::UByteArray> visibility,
RifOpmFlowDeckFile& deckFile );
static std::expected<void, QString> static std::expected<void, QString>
replaceEqualsKeywordIndices( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile ); replaceEqualsKeywordIndices( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );
@@ -113,6 +115,7 @@ private:
static std::expected<void, QString> addOperNumRegionAndOperater( RimEclipseCase* eclipseCase, static std::expected<void, QString> addOperNumRegionAndOperater( RimEclipseCase* eclipseCase,
const RigSimulationInputSettings& settings, const RigSimulationInputSettings& settings,
RifOpmFlowDeckFile& deckFile, RifOpmFlowDeckFile& deckFile,
const std::vector<int>& operNumResult,
int operNumRegion, int operNumRegion,
double porvMultiplier ); double porvMultiplier );
@@ -460,33 +460,24 @@ TEST( RigEclipseCaseDataToolsTest, GenerateBorderResultFromIjkBounds )
auto visibility = RigEclipseCaseDataTools::createVisibilityFromIjkBounds( eclipseCase.p(), minIjk, maxIjk ); auto visibility = RigEclipseCaseDataTools::createVisibilityFromIjkBounds( eclipseCase.p(), minIjk, maxIjk );
ASSERT_FALSE( visibility.isNull() ) << "Visibility should be created successfully"; ASSERT_FALSE( visibility.isNull() ) << "Visibility should be created successfully";
// Generate border result using the custom visibility // Generate border result using the custom visibility (returns vector)
RigEclipseResultTools::generateBorderResult( resultCase.get(), visibility, RiaResultNames::bordnum() ); auto borderResult = RigEclipseResultTools::generateBorderResult( resultCase.get(), visibility );
// Verify that the result was created // Verify that the result was generated
auto resultsData = resultCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL ); ASSERT_GT( borderResult.size(), 0 ) << "Border result should not be empty";
ASSERT_NE( resultsData, nullptr ) << "Results data should not be null";
RigEclipseResultAddress resultAddress( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, RiaResultNames::bordnum() );
ASSERT_TRUE( resultsData->hasResultEntry( resultAddress ) ) << "BORDNUM result should exist";
// Get the result data and verify it contains expected values
const std::vector<double>& resultVector = resultsData->cellScalarResults( resultAddress, 0 );
ASSERT_GT( resultVector.size(), 0 ) << "Result vector should not be empty";
// Count cells with different values // Count cells with different values
int invisibleCells = 0; int invisibleCells = 0;
int borderCells = 0; int borderCells = 0;
int interiorCells = 0; int interiorCells = 0;
for ( double value : resultVector ) for ( int value : borderResult )
{ {
int intValue = static_cast<int>( value ); if ( value == RigEclipseResultTools::BorderType::INVISIBLE_CELL )
if ( intValue == RigEclipseResultTools::BorderType::INVISIBLE_CELL )
invisibleCells++; invisibleCells++;
else if ( intValue == RigEclipseResultTools::BorderType::BORDER_CELL ) else if ( value == RigEclipseResultTools::BorderType::BORDER_CELL )
borderCells++; borderCells++;
else if ( intValue == RigEclipseResultTools::BorderType::INTERIOR_CELL ) else if ( value == RigEclipseResultTools::BorderType::INTERIOR_CELL )
interiorCells++; interiorCells++;
} }
@@ -495,9 +486,9 @@ TEST( RigEclipseCaseDataToolsTest, GenerateBorderResultFromIjkBounds )
ASSERT_GT( borderCells, 0 ) << "Should have border cells (value 1)"; ASSERT_GT( borderCells, 0 ) << "Should have border cells (value 1)";
ASSERT_GE( interiorCells, 0 ) << "May have interior cells (value 2)"; ASSERT_GE( interiorCells, 0 ) << "May have interior cells (value 2)";
// The total should match the number of active cells // The total should match the result vector size
int totalCells = invisibleCells + borderCells + interiorCells; int totalCells = invisibleCells + borderCells + interiorCells;
ASSERT_EQ( totalCells, static_cast<int>( resultVector.size() ) ) << "Total should match result vector size"; ASSERT_EQ( totalCells, static_cast<int>( borderResult.size() ) ) << "Total should match result vector size";
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@@ -532,42 +523,27 @@ TEST( RigEclipseCaseDataToolsTest, GenerateOperNumResultFromBorderResult )
ASSERT_FALSE( visibility.isNull() ) << "Visibility should be created successfully"; ASSERT_FALSE( visibility.isNull() ) << "Visibility should be created successfully";
// Generate border result first (required for OPERNUM generation) // Generate border result first (required for OPERNUM generation)
RigEclipseResultTools::generateBorderResult( resultCase.get(), visibility, RiaResultNames::bordnum() ); auto borderResult = RigEclipseResultTools::generateBorderResult( resultCase.get(), visibility );
// Verify BORDNUM was created // Verify border result was generated
auto resultsData = resultCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL ); ASSERT_GT( borderResult.size(), 0 ) << "Border result should not be empty";
ASSERT_NE( resultsData, nullptr ) << "Results data should not be null";
RigEclipseResultAddress bordnumAddress( RiaDefines::ResultCatType::GENERATED, // Test case 1: Generate OPERNUM with automatic border cell value (no existing OPERNUM, should get 2)
RiaDefines::ResultDataType::INTEGER, auto opernumResult = RigEclipseResultTools::generateOperNumResult( resultCase.get(), borderResult );
RiaResultNames::bordnum() );
ASSERT_TRUE( resultsData->hasResultEntry( bordnumAddress ) ) << "BORDNUM result should exist";
// Test case 1: Generate OPERNUM with automatic border cell value (no existing OPERNUM, should get 1) // Verify OPERNUM was generated
RigEclipseResultTools::generateOperNumResult( resultCase.get() ); ASSERT_EQ( borderResult.size(), opernumResult.size() ) << "BORDNUM and OPERNUM should have same size";
ASSERT_GT( opernumResult.size(), 0 ) << "OPERNUM vector should not be empty";
// Verify OPERNUM was created
RigEclipseResultAddress opernumAddress( RiaDefines::ResultCatType::GENERATED,
RiaDefines::ResultDataType::INTEGER,
RiaResultNames::opernum() );
ASSERT_TRUE( resultsData->hasResultEntry( opernumAddress ) ) << "OPERNUM result should exist";
// Get the result data
const std::vector<double>& bordnumVector = resultsData->cellScalarResults( bordnumAddress, 0 );
const std::vector<double>& opernumVector = resultsData->cellScalarResults( opernumAddress, 0 );
ASSERT_EQ( bordnumVector.size(), opernumVector.size() ) << "BORDNUM and OPERNUM should have same size";
ASSERT_GT( opernumVector.size(), 0 ) << "OPERNUM vector should not be empty";
// Count cells with different values and verify the logic // Count cells with different values and verify the logic
int borderCellsWithOperValue = 0; // BORDNUM=1 cells that should get OPERNUM=1 (max 0 + 1) 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=0 int defaultOperCells = 0; // BORDNUM!=1 cells that should get OPERNUM=1
int borderCellsTotal = 0; // Total BORDNUM=1 cells int borderCellsTotal = 0; // Total BORDNUM=1 cells
for ( size_t i = 0; i < bordnumVector.size(); ++i ) for ( size_t i = 0; i < borderResult.size(); ++i )
{ {
int bordnumValue = static_cast<int>( bordnumVector[i] ); int bordnumValue = borderResult[i];
int opernumValue = static_cast<int>( opernumVector[i] ); int opernumValue = opernumResult[i];
if ( bordnumValue == RigEclipseResultTools::BorderType::BORDER_CELL ) if ( bordnumValue == RigEclipseResultTools::BorderType::BORDER_CELL )
{ {
@@ -610,19 +586,16 @@ TEST( RigEclipseCaseDataToolsTest, GenerateOperNumResultFromBorderResult )
ASSERT_EQ( maxOperValue, 15 ) << "Max OPERNUM value should be 15"; ASSERT_EQ( maxOperValue, 15 ) << "Max OPERNUM value should be 15";
// Now generate OPERNUM again - should use max value + 1 = 16 for border cells // Now generate OPERNUM again - should use max value + 1 = 16 for border cells
RigEclipseResultTools::generateOperNumResult( resultCase.get() ); auto updatedOpernumResult = RigEclipseResultTools::generateOperNumResult( resultCase.get(), borderResult );
// Get updated result
const std::vector<double>& updatedOpernumVector = resultsData->cellScalarResults( opernumAddress, 0 );
// Verify the behavior // Verify the behavior
int preservedValues = 0; int preservedValues = 0;
int modifiedValues = 0; int modifiedValues = 0;
for ( size_t i = 0; i < bordnumVector.size(); ++i ) for ( size_t i = 0; i < borderResult.size(); ++i )
{ {
int bordnumValue = static_cast<int>( bordnumVector[i] ); int bordnumValue = borderResult[i];
int opernumValue = static_cast<int>( updatedOpernumVector[i] ); int opernumValue = updatedOpernumResult[i];
if ( bordnumValue == RigEclipseResultTools::BorderType::BORDER_CELL ) if ( bordnumValue == RigEclipseResultTools::BorderType::BORDER_CELL )
{ {
@@ -647,5 +620,5 @@ TEST( RigEclipseCaseDataToolsTest, GenerateOperNumResultFromBorderResult )
// Total should match vector size // Total should match vector size
int totalChecked = modifiedValues + preservedValues; int totalChecked = modifiedValues + preservedValues;
ASSERT_EQ( totalChecked, static_cast<int>( updatedOpernumVector.size() ) ) << "Should check all cells"; ASSERT_EQ( totalChecked, static_cast<int>( updatedOpernumResult.size() ) ) << "Should check all cells";
} }
@@ -27,11 +27,10 @@
#include "ProjectDataModel/Jobs/RimKeywordFactory.h" #include "ProjectDataModel/Jobs/RimKeywordFactory.h"
#include "RigActiveCellInfo.h"
#include "RigCaseCellResultsData.h" #include "RigCaseCellResultsData.h"
#include "RigEclipseCaseData.h" #include "RigEclipseCaseData.h"
#include "RigEclipseResultAddress.h"
#include "RigMainGrid.h" #include "RigMainGrid.h"
#include "RigSimulationInputTool.h"
#include "RimEclipseResultCase.h" #include "RimEclipseResultCase.h"
#include "opm/input/eclipse/Deck/DeckKeyword.hpp" #include "opm/input/eclipse/Deck/DeckKeyword.hpp"
@@ -108,32 +107,28 @@ TEST( RigEclipseResultToolsTest, BorderCellBcconGeneration )
{ {
size_t cellIndex = grid->cellIndexFromIJK( i, j, k ); size_t cellIndex = grid->cellIndexFromIJK( i, j, k );
( *customVisibility )[cellIndex] = 1; ( *customVisibility )[cellIndex] = 1;
// Set bccon==1 for half of the cells
if ( i > grid->cellCountI() / 2 )
{
bcconValues[cellIndex] = 1;
}
} }
} }
} }
// Step 3: Generate BORDNUM result // Step 3: Generate BORDNUM result (returns vector, doesn't store)
RigEclipseResultTools::generateBorderResult( testCase.get(), customVisibility, "BORDNUM" ); auto borderResult = RigEclipseResultTools::generateBorderResult( testCase.get(), customVisibility );
// Verify BORDNUM was created // Verify border result values
auto resultsData = testCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL ); EXPECT_EQ( borderResult.size(), cellCount );
ASSERT_NE( resultsData, nullptr );
RigEclipseResultAddress bordNumAddr( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, "BORDNUM" ); // Step 4: Generate BCCON result (takes border result as input, returns vector)
ASSERT_TRUE( resultsData->hasResultEntry( bordNumAddr ) ) << "BORDNUM result not created"; caf::VecIjk0 min( startI, startJ, startK );
caf::VecIjk0 max( endI - 1, endJ - 1, endK - 1 );
auto bcconResult = RigEclipseResultTools::generateBcconResult( testCase.get(), borderResult, min, max );
RigEclipseResultTools::createResultVector( *testCase, "BCCON", bcconValues ); // Verify BCCON values
EXPECT_EQ( bcconResult.size(), cellCount );
// Step 4: Generate border cell faces // Step 5: Generate border cell faces using in-memory results
auto borderCellFaces = RigEclipseResultTools::generateBorderCellFaces( testCase.get() ); auto borderCellFaces = RigEclipseResultTools::generateBorderCellFaces( testCase.get(), borderResult, bcconResult );
// Step 5: Verify results // Step 6: Verify results
EXPECT_GT( borderCellFaces.size(), 0 ) << "No border cell faces generated"; EXPECT_GT( borderCellFaces.size(), 0 ) << "No border cell faces generated";
// Verify that all faces are within the visible region boundaries // Verify that all faces are within the visible region boundaries
@@ -150,11 +145,11 @@ TEST( RigEclipseResultToolsTest, BorderCellBcconGeneration )
EXPECT_GE( static_cast<int>( face.faceType ), static_cast<int>( cvf::StructGridInterface::POS_I ) ); EXPECT_GE( static_cast<int>( face.faceType ), static_cast<int>( cvf::StructGridInterface::POS_I ) );
EXPECT_LT( static_cast<int>( face.faceType ), static_cast<int>( cvf::StructGridInterface::NO_FACE ) ); EXPECT_LT( static_cast<int>( face.faceType ), static_cast<int>( cvf::StructGridInterface::NO_FACE ) );
// Verify boundary condition (should be 1 if no BCCON grid property exists) // Verify boundary condition value is set
EXPECT_EQ( face.boundaryCondition, 1 ); EXPECT_GT( face.boundaryCondition, 0 );
} }
// Step 6: Create OPM deck file and add BCCON keyword // Step 7: Create OPM deck file and add BCCON keyword
QTemporaryDir tempDir; QTemporaryDir tempDir;
ASSERT_TRUE( tempDir.isValid() ) << "Failed to create temporary directory"; ASSERT_TRUE( tempDir.isValid() ) << "Failed to create temporary directory";
@@ -184,7 +179,7 @@ TEST( RigEclipseResultToolsTest, BorderCellBcconGeneration )
bool bcconAdded = deckFile.replaceKeyword( "GRID", bcconKw ); bool bcconAdded = deckFile.replaceKeyword( "GRID", bcconKw );
ASSERT_TRUE( bcconAdded ) << "Failed to replace BCCON keyword"; ASSERT_TRUE( bcconAdded ) << "Failed to replace BCCON keyword";
// Step 7: Save deck and verify format // Step 8: Save deck and verify format
QString outputDeckPath = tempDir.filePath( "output_bccon.DATA" ); QString outputDeckPath = tempDir.filePath( "output_bccon.DATA" );
bool deckSaved = deckFile.saveDeck( tempDir.path().toStdString(), "output_bccon.DATA" ); bool deckSaved = deckFile.saveDeck( tempDir.path().toStdString(), "output_bccon.DATA" );
ASSERT_TRUE( deckSaved ) << "Failed to save deck file"; ASSERT_TRUE( deckSaved ) << "Failed to save deck file";
@@ -276,32 +271,15 @@ TEST( RigEclipseResultToolsTest, BcconResultWithFaceNumbering )
} }
} }
// Step 3: Generate BORDNUM result // Step 3: Generate BORDNUM result (returns vector)
RigEclipseResultTools::generateBorderResult( testCase.get(), customVisibility, "BORDNUM" ); auto borderResult = RigEclipseResultTools::generateBorderResult( testCase.get(), customVisibility );
// Verify BORDNUM was created ASSERT_FALSE( borderResult.empty() ) << "Border result is empty";
auto resultsData = testCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
ASSERT_NE( resultsData, nullptr );
RigEclipseResultAddress bordNumAddr( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, "BORDNUM" ); // Step 4: Generate BCCON result (returns vector)
ASSERT_TRUE( resultsData->hasResultEntry( bordNumAddr ) ) << "BORDNUM result not created"; auto bcconResult = RigEclipseResultTools::generateBcconResult( testCase.get(), borderResult, min, max );
// Step 4: Generate BCCON result ASSERT_FALSE( bcconResult.empty() ) << "BCCON result is empty";
RigEclipseResultTools::generateBcconResult( testCase.get(), min, max );
// Verify BCCON was created
RigEclipseResultAddress bcconAddr( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, "BCCON" );
ASSERT_TRUE( resultsData->hasResultEntry( bcconAddr ) ) << "BCCON result not created";
// Get BCCON and BORDNUM values
resultsData->ensureKnownResultLoaded( bcconAddr );
resultsData->ensureKnownResultLoaded( bordNumAddr );
auto bcconValues = resultsData->cellScalarResults( bcconAddr, 0 );
auto bordNumValues = resultsData->cellScalarResults( bordNumAddr, 0 );
ASSERT_FALSE( bcconValues.empty() ) << "BCCON values are empty";
ASSERT_FALSE( bordNumValues.empty() ) << "BORDNUM values are empty";
// Step 5: Verify BCCON values are correct // Step 5: Verify BCCON values are correct
int countI_minus = 0, countI_plus = 0; int countI_minus = 0, countI_plus = 0;
@@ -316,8 +294,8 @@ TEST( RigEclipseResultToolsTest, BcconResultWithFaceNumbering )
{ {
size_t cellIndex = grid->cellIndexFromIJK( i, j, k ); size_t cellIndex = grid->cellIndexFromIJK( i, j, k );
int borderValue = static_cast<int>( bordNumValues[cellIndex] ); int borderValue = borderResult[cellIndex];
int bcconValue = static_cast<int>( bcconValues[cellIndex] ); int bcconValue = bcconResult[cellIndex];
// Only check border cells // Only check border cells
if ( borderValue == RigEclipseResultTools::BorderType::BORDER_CELL ) if ( borderValue == RigEclipseResultTools::BorderType::BORDER_CELL )