#13411 Sector Model Export: unify BOX/ENDBOX processing into single pass

Replace 7 separate deck iterations (cropDataKeywordsInsideBoxContext,
expandBoxContextInDeckFile, and 5 replace*KeywordIndices wrappers) with
a single transformKeywordsInDeckFile function that tracks BOX/ENDBOX
state once and handles all keyword types as they are encountered.
This commit is contained in:
Kristian Bendiksen committed 2026-03-20 14:30:34 +01:00
1 parent fce37b8fdb
commit 539eec6cc5
3 files changed
+196 -250

No files matched your search

@@ -101,8 +101,6 @@ std::expected<void, QString> RigSimulationInputTool::exportSimulationInput( RimE
return result; return result;
} }
cropDataKeywordsInsideBoxContext( deckFile, settings.min(), settings.max(), settings.refinement() );
auto croppedKeywords = cropDataKeywordsInDeckFile( &eclipseCase, settings, deckFile ); auto croppedKeywords = cropDataKeywordsInDeckFile( &eclipseCase, settings, deckFile );
if ( auto result = replaceKeywordValuesInDeckFile( &eclipseCase, settings, deckFile, croppedKeywords ); !result ) if ( auto result = replaceKeywordValuesInDeckFile( &eclipseCase, settings, deckFile, croppedKeywords ); !result )
@@ -110,31 +108,9 @@ std::expected<void, QString> RigSimulationInputTool::exportSimulationInput( RimE
return result; return result;
} }
// Pre-process BOX/ENDBOX context: inject BOX indices into EQUALS/COPY/ADD/MULTIPLY records // Single-pass BOX/ENDBOX processing: crop data keywords, inject+transform EQUALS/COPY/ADD/MULTIPLY,
// that don't have explicit box indices, so they get properly transformed by subsequent steps. // and transform BOX coordinates, all while tracking BOX/ENDBOX state.
expandBoxContextInDeckFile( deckFile ); if ( auto result = transformKeywordsInDeckFile( &eclipseCase, settings, deckFile ); !result )
if ( auto result = replaceEqualsKeywordIndices( &eclipseCase, settings, deckFile ); !result )
{
return result;
}
if ( auto result = replaceCopyKeywordIndices( &eclipseCase, settings, deckFile ); !result )
{
return result;
}
if ( auto result = replaceMultiplyKeywordIndices( &eclipseCase, settings, deckFile ); !result )
{
return result;
}
if ( auto result = replaceAddKeywordIndices( &eclipseCase, settings, deckFile ); !result )
{
return result;
}
if ( auto result = replaceBoxKeywordIndices( &eclipseCase, settings, deckFile ); !result )
{ {
return result; return result;
} }
@@ -602,19 +578,47 @@ static std::array<int, 6> extractBoxIndicesFromRecord( const Opm::DeckRecord& re
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Pre-processing step: expand BOX context into EQUALS/COPY/ADD/MULTIPLY records. /// Single-pass BOX/ENDBOX processing: tracks BOX/ENDBOX state and handles all keyword types
/// For keywords inside a BOX/ENDBOX block, records without explicit box indices /// in one iteration. Crops data keywords to sector/box intersection, injects BOX indices into
/// get the active BOX indices injected. This ensures all records have explicit /// EQUALS/COPY/ADD/MULTIPLY records and transforms their coordinates, and transforms BOX
/// indices before the individual keyword processing transforms them. /// coordinates. Replaces the previous multi-pass approach (expandBoxContextInDeckFile +
/// cropDataKeywordsInsideBoxContext + replace*KeywordIndices).
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
void RigSimulationInputTool::expandBoxContextInDeckFile( RifOpmFlowDeckFile& deckFile ) std::expected<void, QString> RigSimulationInputTool::transformKeywordsInDeckFile( RimEclipseCase* eclipseCase,
const RigSimulationInputSettings& settings,
RifOpmFlowDeckFile& deckFile )
{ {
Opm::FileDeck* fd = deckFile.fileDeck(); Opm::FileDeck* fd = deckFile.fileDeck();
if ( !fd ) return {}; if ( !fd ) return {};
bool insideBox = false; const auto sectorMin = settings.min();
std::array<int, 6> activeBoxIndices{}; const auto sectorMax = settings.max();
const std::set<std::string> affectedKeywords = { "EQUALS", "COPY", "ADD", "MULTIPLY" }; const auto refinement = settings.refinement();
// Map keyword names to their record processor functions
const std::map<std::string, RecordProcessorFunc> recordProcessors = {
{ "EQUALS", processEqualsRecord },
{ "COPY", processCopyRecord },
{ "ADD", processAddRecord },
{ "MULTIPLY", processMultiplyRecord },
};
struct Modification
{
Opm::FileDeck::Index index;
enum Action
{
Replace,
Remove
} action;
Opm::DeckKeyword replacement;
};
bool insideBox = false;
std::array<int, 6> activeBoxIndices{};
caf::VecIjk0 boxMin( 0, 0, 0 );
caf::VecIjk0 boxMax( 0, 0, 0 );
std::vector<Modification> modifications;
for ( auto it = fd->start(); it != fd->stop(); it++ ) for ( auto it = fd->start(); it != fd->stop(); it++ )
{ {
@@ -628,6 +632,7 @@ void RigSimulationInputTool::expandBoxContextInDeckFile( RifOpmFlowDeckFile& dec
RiaLogging::warning( "BOX keyword found while already inside a BOX context. Previous BOX will be overridden." ); RiaLogging::warning( "BOX keyword found while already inside a BOX context. Previous BOX will be overridden." );
} }
insideBox = true;
if ( kw.size() > 0 ) if ( kw.size() > 0 )
{ {
const auto& rec = kw.getRecord( 0 ); const auto& rec = kw.getRecord( 0 );
@@ -639,9 +644,36 @@ void RigSimulationInputTool::expandBoxContextInDeckFile( RifOpmFlowDeckFile& dec
rec.getItem( 3 ).get<int>( 0 ), rec.getItem( 3 ).get<int>( 0 ),
rec.getItem( 4 ).get<int>( 0 ), rec.getItem( 4 ).get<int>( 0 ),
rec.getItem( 5 ).get<int>( 0 ) }; rec.getItem( 5 ).get<int>( 0 ) };
insideBox = true;
// BOX items: I1, I2, J1, J2, K1, K2 (1-based) -> 0-based for data keyword cropping
boxMin = caf::VecIjk0( activeBoxIndices[0] - 1, activeBoxIndices[2] - 1, activeBoxIndices[4] - 1 );
boxMax = caf::VecIjk0( activeBoxIndices[1] - 1, activeBoxIndices[3] - 1, activeBoxIndices[5] - 1 );
} }
} }
// Transform BOX coordinates via processBoxRecord
try
{
if ( kw.size() > 0 )
{
auto result = processBoxRecord( kw.getRecord( 0 ), sectorMin, sectorMax, refinement );
if ( result )
{
Opm::DeckKeyword transformedKw( kw.location(), kw.name() );
transformedKw.addRecord( std::move( result.value() ) );
modifications.push_back( { it, Modification::Replace, std::move( transformedKw ) } );
}
else
{
modifications.push_back( { it, Modification::Remove, Opm::DeckKeyword{} } );
RiaLogging::warning( QString( "Failed to process BOX record: %1" ).arg( result.error() ) );
}
}
}
catch ( std::exception& e )
{
return std::unexpected( QString( "Exception processing BOX keyword: %1" ).arg( e.what() ) );
}
} }
else if ( name == "ENDBOX" ) else if ( name == "ENDBOX" )
{ {
@@ -651,110 +683,9 @@ void RigSimulationInputTool::expandBoxContextInDeckFile( RifOpmFlowDeckFile& dec
} }
insideBox = false; insideBox = false;
} }
else if ( insideBox && affectedKeywords.count( name ) )
{
// Check if any record in this keyword needs expansion
bool needsExpansion = false;
for ( size_t r = 0; r < kw.size(); ++r )
{
if ( !recordHasExplicitBoxIndices( kw.getRecord( r ) ) )
{
needsExpansion = true;
break;
}
}
if ( needsExpansion )
{
Opm::DeckKeyword expandedKw( kw.location(), kw.name() );
// Track "current operation box" - starts with active BOX indices
std::array<int, 6> currentOpBox = activeBoxIndices;
for ( size_t r = 0; r < kw.size(); ++r )
{
const auto& rec = kw.getRecord( r );
if ( recordHasExplicitBoxIndices( rec ) )
{
// Update current operation box from this record's explicit indices
currentOpBox = extractBoxIndicesFromRecord( rec );
Opm::DeckRecord copy( rec );
expandedKw.addRecord( std::move( copy ) );
}
else
{
// Inject current operation box indices into this record
expandedKw.addRecord( injectBoxIndicesIntoRecord( rec, currentOpBox ) );
}
}
deckFile.replaceKeywordAtIndex( it, expandedKw );
}
}
}
if ( insideBox )
{
RiaLogging::warning( "BOX keyword found without a matching ENDBOX at end of deck." );
}
}
//--------------------------------------------------------------------------------------------------
/// Crop data keywords (PORO, EQLNUM, PERMX, etc.) inside BOX/ENDBOX blocks to the intersection
/// of the BOX region with the sector. If the box does not intersect the sector, the data keyword
/// is removed. This preserves the BOX/ENDBOX structure in the output while ensuring data sizes
/// match the transformed box dimensions.
//--------------------------------------------------------------------------------------------------
void RigSimulationInputTool::cropDataKeywordsInsideBoxContext( RifOpmFlowDeckFile& deckFile,
const caf::VecIjk0& sectorMin,
const caf::VecIjk0& sectorMax,
const cvf::Vec3st& refinement )
{
Opm::FileDeck* fd = deckFile.fileDeck();
if ( !fd ) return;
struct Modification
{
Opm::FileDeck::Index index;
bool remove = false;
Opm::DeckKeyword replacement;
};
bool insideBox = false;
caf::VecIjk0 boxMin( 0, 0, 0 );
caf::VecIjk0 boxMax( 0, 0, 0 );
std::vector<Modification> modifications;
for ( auto it = fd->start(); it != fd->stop(); it++ )
{
const auto& kw = ( *fd )[it];
const std::string& name = kw.name();
if ( name == "BOX" )
{
insideBox = true;
if ( kw.size() > 0 )
{
const auto& rec = kw.getRecord( 0 );
if ( rec.size() >= 6 )
{
// BOX items: I1, I2, J1, J2, K1, K2 (1-based)
boxMin = caf::VecIjk0( rec.getItem( 0 ).get<int>( 0 ) - 1,
rec.getItem( 2 ).get<int>( 0 ) - 1,
rec.getItem( 4 ).get<int>( 0 ) - 1 );
boxMax = caf::VecIjk0( rec.getItem( 1 ).get<int>( 0 ) - 1,
rec.getItem( 3 ).get<int>( 0 ) - 1,
rec.getItem( 5 ).get<int>( 0 ) - 1 );
}
}
}
else if ( name == "ENDBOX" )
{
insideBox = false;
}
else if ( insideBox && kw.isDataKeyword() ) else if ( insideBox && kw.isDataKeyword() )
{ {
// Compute intersection of box with sector (0-based, inclusive) // Crop data keyword to sector/box intersection
caf::VecIjk0 intMin( std::max( boxMin.x(), sectorMin.x() ), caf::VecIjk0 intMin( std::max( boxMin.x(), sectorMin.x() ),
std::max( boxMin.y(), sectorMin.y() ), std::max( boxMin.y(), sectorMin.y() ),
std::max( boxMin.z(), sectorMin.z() ) ); std::max( boxMin.z(), sectorMin.z() ) );
@@ -764,8 +695,7 @@ void RigSimulationInputTool::cropDataKeywordsInsideBoxContext( RifOpmFlowDeckFil
if ( intMin.x() > intMax.x() || intMin.y() > intMax.y() || intMin.z() > intMax.z() ) if ( intMin.x() > intMax.x() || intMin.y() > intMax.y() || intMin.z() > intMax.z() )
{ {
// No intersection - remove the data keyword modifications.push_back( { it, Modification::Remove, Opm::DeckKeyword{} } );
modifications.push_back( { it, true, Opm::DeckKeyword{} } );
RiaLogging::info( QString( "Removing data keyword '%1' inside BOX/ENDBOX: box does not intersect sector" ) RiaLogging::info( QString( "Removing data keyword '%1' inside BOX/ENDBOX: box does not intersect sector" )
.arg( QString::fromStdString( name ) ) ); .arg( QString::fromStdString( name ) ) );
continue; continue;
@@ -775,7 +705,6 @@ void RigSimulationInputTool::cropDataKeywordsInsideBoxContext( RifOpmFlowDeckFil
const auto& item = kw.getRecord( 0 ).getItem( 0 ); const auto& item = kw.getRecord( 0 ).getItem( 0 );
// Box dimensions (source grid for the data)
const size_t boxNi = boxMax.x() - boxMin.x() + 1; const size_t boxNi = boxMax.x() - boxMin.x() + 1;
const size_t boxNj = boxMax.y() - boxMin.y() + 1; const size_t boxNj = boxMax.y() - boxMin.y() + 1;
@@ -783,7 +712,6 @@ void RigSimulationInputTool::cropDataKeywordsInsideBoxContext( RifOpmFlowDeckFil
{ {
std::vector<T> croppedData; std::vector<T> croppedData;
// Iterate over refined intersection cells
for ( size_t k = intMin.z(); k <= static_cast<size_t>( intMax.z() ); ++k ) for ( size_t k = intMin.z(); k <= static_cast<size_t>( intMax.z() ); ++k )
{ {
for ( size_t rk = 0; rk < refinement.z(); ++rk ) for ( size_t rk = 0; rk < refinement.z(); ++rk )
@@ -794,11 +722,10 @@ void RigSimulationInputTool::cropDataKeywordsInsideBoxContext( RifOpmFlowDeckFil
{ {
for ( size_t i = intMin.x(); i <= static_cast<size_t>( intMax.x() ); ++i ) for ( size_t i = intMin.x(); i <= static_cast<size_t>( intMax.x() ); ++i )
{ {
// Box-relative indices size_t boxRelI = i - boxMin.x();
size_t boxRelI = i - boxMin.x(); size_t boxRelJ = j - boxMin.y();
size_t boxRelJ = j - boxMin.y(); size_t boxRelK = k - boxMin.z();
size_t boxRelK = k - boxMin.z(); size_t srcIdx = boxRelI + boxRelJ * boxNi + boxRelK * boxNi * boxNj;
size_t srcIdx = boxRelI + boxRelJ * boxNi + boxRelK * boxNi * boxNj;
for ( size_t ri = 0; ri < refinement.x(); ++ri ) for ( size_t ri = 0; ri < refinement.x(); ++ri )
{ {
@@ -810,7 +737,6 @@ void RigSimulationInputTool::cropDataKeywordsInsideBoxContext( RifOpmFlowDeckFil
} }
} }
// Build replacement keyword
Opm::DeckKeyword newKw = kw.emptyStructuralCopy(); Opm::DeckKeyword newKw = kw.emptyStructuralCopy();
Opm::DeckRecord record; Opm::DeckRecord record;
@@ -832,7 +758,7 @@ void RigSimulationInputTool::cropDataKeywordsInsideBoxContext( RifOpmFlowDeckFil
} }
newKw.addRecord( std::move( record ) ); newKw.addRecord( std::move( record ) );
modifications.push_back( { it, false, std::move( newKw ) } ); modifications.push_back( { it, Modification::Replace, std::move( newKw ) } );
RiaLogging::info( QString( "Cropped data keyword '%1' inside BOX/ENDBOX (%2 -> %3 values)" ) RiaLogging::info( QString( "Cropped data keyword '%1' inside BOX/ENDBOX (%2 -> %3 values)" )
.arg( QString::fromStdString( name ) ) .arg( QString::fromStdString( name ) )
@@ -849,18 +775,82 @@ void RigSimulationInputTool::cropDataKeywordsInsideBoxContext( RifOpmFlowDeckFil
cropBoxData( kw.getRawDoubleData() ); cropBoxData( kw.getRawDoubleData() );
} }
} }
else if ( auto procIt = recordProcessors.find( name ); procIt != recordProcessors.end() )
{
// EQUALS, COPY, ADD, MULTIPLY keyword
const auto& processorFunc = procIt->second;
try
{
Opm::DeckKeyword transformedKw( kw.location(), kw.name() );
// Track "current operation box" for records inside BOX context
std::array<int, 6> currentOpBox = activeBoxIndices;
for ( size_t r = 0; r < kw.size(); ++r )
{
const auto& rec = kw.getRecord( r );
Opm::DeckRecord recordToProcess = rec;
if ( insideBox )
{
// Inside BOX: inject box indices into records without explicit coords
if ( recordHasExplicitBoxIndices( rec ) )
{
currentOpBox = extractBoxIndicesFromRecord( rec );
}
else
{
recordToProcess = injectBoxIndicesIntoRecord( rec, currentOpBox );
}
}
// Transform the record
auto result = processorFunc( recordToProcess, sectorMin, sectorMax, refinement );
if ( result )
{
transformedKw.addRecord( std::move( result.value() ) );
}
else
{
RiaLogging::warning(
QString( "Failed to process %1 record: %2" ).arg( QString::fromStdString( name ) ).arg( result.error() ) );
}
}
if ( transformedKw.size() > 0 )
{
modifications.push_back( { it, Modification::Replace, std::move( transformedKw ) } );
}
else
{
// All records failed - replace with SKIP
modifications.push_back( { it, Modification::Replace, Opm::DeckKeyword( kw.location(), "SKIP" ) } );
}
}
catch ( std::exception& e )
{
return std::unexpected( QString( "Exception processing %1 keyword: %2" ).arg( QString::fromStdString( name ) ).arg( e.what() ) );
}
}
}
if ( insideBox )
{
RiaLogging::warning( "BOX keyword found without a matching ENDBOX at end of deck." );
} }
// Apply modifications in reverse order to maintain valid indices // Apply modifications in reverse order to maintain valid indices
for ( auto it = modifications.rbegin(); it != modifications.rend(); ++it ) for ( auto modIt = modifications.rbegin(); modIt != modifications.rend(); ++modIt )
{ {
if ( it->remove ) if ( modIt->action == Modification::Remove )
{ {
deckFile.removeKeywordAtIndex( it->index ); deckFile.removeKeywordAtIndex( modIt->index );
} }
else else
{ {
deckFile.replaceKeywordAtIndex( it->index, it->replacement ); deckFile.replaceKeywordAtIndex( modIt->index, modIt->replacement );
} }
} }
@@ -929,46 +919,6 @@ std::expected<void, QString> RigSimulationInputTool::replaceKeywordWithBoxIndice
return {}; return {};
} }
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<void, QString> RigSimulationInputTool::replaceEqualsKeywordIndices( RimEclipseCase* eclipseCase,
const RigSimulationInputSettings& settings,
RifOpmFlowDeckFile& deckFile )
{
return replaceKeywordWithBoxIndices( "EQUALS", eclipseCase, settings, deckFile, processEqualsRecord );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<void, QString> RigSimulationInputTool::replaceMultiplyKeywordIndices( RimEclipseCase* eclipseCase,
const RigSimulationInputSettings& settings,
RifOpmFlowDeckFile& deckFile )
{
return replaceKeywordWithBoxIndices( "MULTIPLY", eclipseCase, settings, deckFile, processMultiplyRecord );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<void, QString> RigSimulationInputTool::replaceCopyKeywordIndices( RimEclipseCase* eclipseCase,
const RigSimulationInputSettings& settings,
RifOpmFlowDeckFile& deckFile )
{
return replaceKeywordWithBoxIndices( "COPY", eclipseCase, settings, deckFile, processCopyRecord );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<void, QString> RigSimulationInputTool::replaceAddKeywordIndices( RimEclipseCase* eclipseCase,
const RigSimulationInputSettings& settings,
RifOpmFlowDeckFile& deckFile )
{
return replaceKeywordWithBoxIndices( "ADD", eclipseCase, settings, deckFile, processAddRecord );
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// ///
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@@ -999,16 +949,6 @@ std::expected<void, QString> RigSimulationInputTool::replaceAqunumKeywordIndices
return replaceKeywordWithBoxIndices( "AQUNUM", eclipseCase, settings, deckFile, processAqunumRecord ); return replaceKeywordWithBoxIndices( "AQUNUM", eclipseCase, settings, deckFile, processAqunumRecord );
} }
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::expected<void, QString> RigSimulationInputTool::replaceBoxKeywordIndices( RimEclipseCase* eclipseCase,
const RigSimulationInputSettings& settings,
RifOpmFlowDeckFile& deckFile )
{
return replaceKeywordWithBoxIndices( "BOX", eclipseCase, settings, deckFile, processBoxRecord );
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// ///
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@@ -26,6 +26,7 @@
#include <expected> #include <expected>
#include <functional> #include <functional>
#include <map>
#include <set> #include <set>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -102,11 +103,8 @@ public:
static std::vector<int> createRefinedVisibility( const RigGridExportAdapter& gridAdapter ); static std::vector<int> createRefinedVisibility( const RigGridExportAdapter& gridAdapter );
static void expandBoxContextInDeckFile( RifOpmFlowDeckFile& deckFile ); static std::expected<void, QString>
static void cropDataKeywordsInsideBoxContext( RifOpmFlowDeckFile& deckFile, transformKeywordsInDeckFile( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );
const caf::VecIjk0& sectorMin,
const caf::VecIjk0& sectorMax,
const cvf::Vec3st& refinement );
// Public EDITNNC methods for testing // Public EDITNNC methods for testing
static std::vector<NNCConnection> extractDeckEditNncConnections( RifOpmFlowDeckFile& deckFile, const RigMainGrid& mainGrid ); static std::vector<NNCConnection> extractDeckEditNncConnections( RifOpmFlowDeckFile& deckFile, const RigMainGrid& mainGrid );
@@ -144,21 +142,6 @@ private:
cvf::ref<cvf::UByteArray> visibility, cvf::ref<cvf::UByteArray> visibility,
RifOpmFlowDeckFile& deckFile ); RifOpmFlowDeckFile& deckFile );
static std::expected<void, QString>
replaceEqualsKeywordIndices( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );
static std::expected<void, QString>
replaceMultiplyKeywordIndices( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );
static std::expected<void, QString>
replaceBoxKeywordIndices( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );
static std::expected<void, QString>
replaceCopyKeywordIndices( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );
static std::expected<void, QString>
replaceAddKeywordIndices( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );
static std::expected<void, QString> static std::expected<void, QString>
replaceAquconKeywordIndices( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile ); replaceAquconKeywordIndices( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );
@@ -1822,11 +1822,12 @@ TEST( RigSimulationInputTool, ProcessMultiplyRecord_WithBoxIndices )
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Test expandBoxContextInDeckFile: EQUALS inside BOX/ENDBOX with no explicit indices /// Test transformKeywordsInDeckFile: EQUALS inside BOX/ENDBOX with no explicit indices
/// gets BOX indices injected and then transformed to sector-relative coordinates.
/// Using sector covering the whole grid so transformed coords = original coords.
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
TEST( RigSimulationInputTool, ExpandBoxContext_EqualsInheritsBoxIndices ) TEST( RigSimulationInputTool, ExpandBoxContext_EqualsInheritsBoxIndices )
{ {
// Create a minimal DATA file with BOX/ENDBOX containing EQUALS without indices
QTemporaryDir tempDir; QTemporaryDir tempDir;
ASSERT_TRUE( tempDir.isValid() ); ASSERT_TRUE( tempDir.isValid() );
@@ -1847,20 +1848,24 @@ TEST( RigSimulationInputTool, ExpandBoxContext_EqualsInheritsBoxIndices )
auto loadResult = deckFile.loadDeck( dataFilePath.toStdString() ); auto loadResult = deckFile.loadDeck( dataFilePath.toStdString() );
ASSERT_TRUE( loadResult.has_value() ) << loadResult.error(); ASSERT_TRUE( loadResult.has_value() ) << loadResult.error();
// Verify EQUALS has no explicit box indices before expansion // Verify EQUALS has no explicit box indices before transformation
auto equalsBeforeVec = deckFile.findAllKeywordsWithIndices( "EQUALS" ); auto equalsBeforeVec = deckFile.findAllKeywordsWithIndices( "EQUALS" );
ASSERT_EQ( 1u, equalsBeforeVec.size() ); ASSERT_EQ( 1u, equalsBeforeVec.size() );
ASSERT_EQ( 1u, equalsBeforeVec[0].second.size() ); ASSERT_EQ( 1u, equalsBeforeVec[0].second.size() );
// The record should have fewer than 8 items or items without values const auto& recBefore = equalsBeforeVec[0].second.getRecord( 0 );
const auto& recBefore = equalsBeforeVec[0].second.getRecord( 0 ); bool hadExplicitBefore = recBefore.size() >= 8 && recBefore.getItem( 2 ).hasValue( 0 ) && recBefore.getItem( 3 ).hasValue( 0 ) &&
bool hadExplicitBefore = recBefore.getItem( 4 ).hasValue( 0 ) && recBefore.getItem( 5 ).hasValue( 0 ) &&
recBefore.size() >= 8 && recBefore.getItem( 2 ).hasValue( 0 ) && recBefore.getItem( 3 ).hasValue( 0 ) && recBefore.getItem( 6 ).hasValue( 0 ) && recBefore.getItem( 7 ).hasValue( 0 );
recBefore.getItem( 4 ).hasValue( 0 ) && recBefore.getItem( 5 ).hasValue( 0 ) && recBefore.getItem( 6 ).hasValue( 0 ) && EXPECT_FALSE( hadExplicitBefore ) << "Record should not have explicit box indices before transformation";
recBefore.getItem( 7 ).hasValue( 0 );
EXPECT_FALSE( hadExplicitBefore ) << "Record should not have explicit box indices before expansion";
// Run expansion // Sector covers entire grid: sectorMin=(0,0,0), sectorMax=(9,9,4), refinement=(1,1,1)
RigSimulationInputTool::expandBoxContextInDeckFile( deckFile ); // This makes coordinate transformation an identity operation
RigSimulationInputSettings settings;
settings.setMin( caf::VecIjk0( 0, 0, 0 ) );
settings.setMax( caf::VecIjk0( 9, 9, 4 ) );
settings.setRefinement( cvf::Vec3st( 1, 1, 1 ) );
RigSimulationInputTool::transformKeywordsInDeckFile( nullptr, settings, deckFile );
// Verify EQUALS now has explicit box indices matching the BOX keyword // Verify EQUALS now has explicit box indices matching the BOX keyword
auto equalsAfterVec = deckFile.findAllKeywordsWithIndices( "EQUALS" ); auto equalsAfterVec = deckFile.findAllKeywordsWithIndices( "EQUALS" );
@@ -1879,7 +1884,8 @@ TEST( RigSimulationInputTool, ExpandBoxContext_EqualsInheritsBoxIndices )
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Test expandBoxContextInDeckFile: records with explicit indices keep their own indices /// Test transformKeywordsInDeckFile: records with explicit indices keep their own indices
/// (not overridden by BOX indices) and are transformed to sector-relative coordinates.
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
TEST( RigSimulationInputTool, ExpandBoxContext_ExplicitIndicesPreserved ) TEST( RigSimulationInputTool, ExpandBoxContext_ExplicitIndicesPreserved )
{ {
@@ -1903,7 +1909,13 @@ TEST( RigSimulationInputTool, ExpandBoxContext_ExplicitIndicesPreserved )
auto loadResult = deckFile.loadDeck( dataFilePath.toStdString() ); auto loadResult = deckFile.loadDeck( dataFilePath.toStdString() );
ASSERT_TRUE( loadResult.has_value() ) << loadResult.error(); ASSERT_TRUE( loadResult.has_value() ) << loadResult.error();
RigSimulationInputTool::expandBoxContextInDeckFile( deckFile ); // Sector covers entire grid
RigSimulationInputSettings settings;
settings.setMin( caf::VecIjk0( 0, 0, 0 ) );
settings.setMax( caf::VecIjk0( 9, 9, 4 ) );
settings.setRefinement( cvf::Vec3st( 1, 1, 1 ) );
RigSimulationInputTool::transformKeywordsInDeckFile( nullptr, settings, deckFile );
auto equalsVec = deckFile.findAllKeywordsWithIndices( "EQUALS" ); auto equalsVec = deckFile.findAllKeywordsWithIndices( "EQUALS" );
ASSERT_EQ( 1u, equalsVec.size() ); ASSERT_EQ( 1u, equalsVec.size() );
@@ -1921,7 +1933,8 @@ TEST( RigSimulationInputTool, ExpandBoxContext_ExplicitIndicesPreserved )
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Test expandBoxContextInDeckFile: keywords outside BOX context are not modified /// Test transformKeywordsInDeckFile: EQUALS outside BOX context without explicit indices
/// passes through unchanged (no box indices injected).
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
TEST( RigSimulationInputTool, ExpandBoxContext_OutsideBoxNotModified ) TEST( RigSimulationInputTool, ExpandBoxContext_OutsideBoxNotModified )
{ {
@@ -1943,7 +1956,13 @@ TEST( RigSimulationInputTool, ExpandBoxContext_OutsideBoxNotModified )
auto loadResult = deckFile.loadDeck( dataFilePath.toStdString() ); auto loadResult = deckFile.loadDeck( dataFilePath.toStdString() );
ASSERT_TRUE( loadResult.has_value() ) << loadResult.error(); ASSERT_TRUE( loadResult.has_value() ) << loadResult.error();
RigSimulationInputTool::expandBoxContextInDeckFile( deckFile ); // Sector covers entire grid
RigSimulationInputSettings settings;
settings.setMin( caf::VecIjk0( 0, 0, 0 ) );
settings.setMax( caf::VecIjk0( 9, 9, 4 ) );
settings.setRefinement( cvf::Vec3st( 1, 1, 1 ) );
RigSimulationInputTool::transformKeywordsInDeckFile( nullptr, settings, deckFile );
auto equalsVec = deckFile.findAllKeywordsWithIndices( "EQUALS" ); auto equalsVec = deckFile.findAllKeywordsWithIndices( "EQUALS" );
ASSERT_EQ( 1u, equalsVec.size() ); ASSERT_EQ( 1u, equalsVec.size() );
@@ -1957,7 +1976,7 @@ TEST( RigSimulationInputTool, ExpandBoxContext_OutsideBoxNotModified )
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Test cropDataKeywordsInsideBoxContext: data keywords inside BOX/ENDBOX are cropped /// Test transformKeywordsInDeckFile: data keywords inside BOX/ENDBOX are cropped
/// to the intersection of the box with the sector /// to the intersection of the box with the sector
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
TEST( RigSimulationInputTool, CropDataKeywordsInsideBoxContext ) TEST( RigSimulationInputTool, CropDataKeywordsInsideBoxContext )
@@ -1993,11 +2012,13 @@ TEST( RigSimulationInputTool, CropDataKeywordsInsideBoxContext )
auto eqlnumBefore = deckFile.findAllKeywordsWithIndices( "EQLNUM" ); auto eqlnumBefore = deckFile.findAllKeywordsWithIndices( "EQLNUM" );
EXPECT_EQ( 2u, eqlnumBefore.size() ); EXPECT_EQ( 2u, eqlnumBefore.size() );
// Run cropping with sector (2,2,1)-(7,7,4), no refinement // Run transformation with sector (2,2,1)-(7,7,4), no refinement
caf::VecIjk0 sectorMin( 2, 2, 1 ); RigSimulationInputSettings settings;
caf::VecIjk0 sectorMax( 7, 7, 4 ); settings.setMin( caf::VecIjk0( 2, 2, 1 ) );
cvf::Vec3st refinement( 1, 1, 1 ); settings.setMax( caf::VecIjk0( 7, 7, 4 ) );
RigSimulationInputTool::cropDataKeywordsInsideBoxContext( deckFile, sectorMin, sectorMax, refinement ); settings.setRefinement( cvf::Vec3st( 1, 1, 1 ) );
RigSimulationInputTool::transformKeywordsInDeckFile( nullptr, settings, deckFile );
// After cropping: should still find two EQLNUM keywords // After cropping: should still find two EQLNUM keywords
auto eqlnumAfter = deckFile.findAllKeywordsWithIndices( "EQLNUM" ); auto eqlnumAfter = deckFile.findAllKeywordsWithIndices( "EQLNUM" );
@@ -2024,7 +2045,7 @@ TEST( RigSimulationInputTool, CropDataKeywordsInsideBoxContext )
} }
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Test cropDataKeywordsInsideBoxContext: box that doesn't intersect sector removes keyword /// Test transformKeywordsInDeckFile: box that doesn't intersect sector removes data keyword
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
TEST( RigSimulationInputTool, CropDataKeywordsInsideBoxContext_NoIntersection ) TEST( RigSimulationInputTool, CropDataKeywordsInsideBoxContext_NoIntersection )
{ {
@@ -2053,10 +2074,12 @@ TEST( RigSimulationInputTool, CropDataKeywordsInsideBoxContext_NoIntersection )
ASSERT_TRUE( loadResult.has_value() ) << loadResult.error(); ASSERT_TRUE( loadResult.has_value() ) << loadResult.error();
// Sector does not include K=4 // Sector does not include K=4
caf::VecIjk0 sectorMin( 2, 2, 0 ); RigSimulationInputSettings settings;
caf::VecIjk0 sectorMax( 7, 7, 2 ); settings.setMin( caf::VecIjk0( 2, 2, 0 ) );
cvf::Vec3st refinement( 1, 1, 1 ); settings.setMax( caf::VecIjk0( 7, 7, 2 ) );
RigSimulationInputTool::cropDataKeywordsInsideBoxContext( deckFile, sectorMin, sectorMax, refinement ); settings.setRefinement( cvf::Vec3st( 1, 1, 1 ) );
RigSimulationInputTool::transformKeywordsInDeckFile( nullptr, settings, deckFile );
// Boxed EQLNUM should be removed since box doesn't intersect sector // Boxed EQLNUM should be removed since box doesn't intersect sector
auto eqlnumAfter = deckFile.findAllKeywordsWithIndices( "EQLNUM" ); auto eqlnumAfter = deckFile.findAllKeywordsWithIndices( "EQLNUM" );