From 788e054734a2c07c0224ddda4afc35f875e88faf Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Wed, 29 Oct 2025 14:26:02 +0100 Subject: [PATCH] Export sector model: Set opernum in regdims. --- .../RicExportEclipseSectorModelFeature.cpp | 70 +++++++++++++++++++ .../FileInterface/RifOpmFlowDeckFile.cpp | 26 +++++-- .../FileInterface/RifOpmFlowDeckFile.h | 8 ++- .../RigEclipseResultTools.cpp | 6 +- .../RigEclipseResultTools.h | 2 +- .../UnitTests/RifOpmFlowDeckFile-Test.cpp | 21 +++--- 6 files changed, 116 insertions(+), 17 deletions(-) diff --git a/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelFeature.cpp b/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelFeature.cpp index a3ca9d4813..1fda9fc4e8 100644 --- a/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelFeature.cpp +++ b/ApplicationLibCode/Commands/ExportCommands/RicExportEclipseSectorModelFeature.cpp @@ -353,6 +353,76 @@ std::expected RicExportEclipseSectorModelFeature::exportSimulatio { return result; } + + // Update REGDIMS and add OPERATER keyword for OPERNUM regions + // Get the OPERNUM region number that was assigned to border cells + int maxOperNum = RigEclipseResultTools::findMaxOperNumValue( &eclipseCase ); + int operNumRegion = maxOperNum; // Border cells use this region number + + RiaLogging::info( QString( "Using OPERNUM region %1 for border cells" ).arg( operNumRegion ) ); + + // Ensure REGDIMS keyword exists + if ( !deckFile.ensureRegdimsKeyword() ) + { + return std::unexpected( "Failed to ensure REGDIMS keyword exists in RUNSPEC section" ); + } + + // Read current REGDIMS values + auto regdimsValues = deckFile.regdims(); + if ( regdimsValues.empty() ) + { + // Use defaults if reading failed + regdimsValues = { 1, 1, 0, 0, 0, 1, 0 }; // NTFIP NMFIPR NRFREG NTFREG MAX_ETRACK NTCREG MAX_OPERNUM + } + + // Update MAX_OPERNUM (item 7) to the new region number + if ( regdimsValues.size() < 7 ) + { + regdimsValues.resize( 7, 0 ); + } + regdimsValues[6] = operNumRegion; // Index 6 is item 7 (MAX_OPERNUM) + + // Update REGDIMS in deck + if ( !deckFile.setRegdims( regdimsValues[0], + regdimsValues[1], + regdimsValues[2], + regdimsValues[3], + regdimsValues[4], + regdimsValues[5], + regdimsValues[6] ) ) + { + return std::unexpected( "Failed to update REGDIMS keyword in deck file" ); + } + + // Check if OPERNUM keyword already exists in deck + auto opernumKeyword = deckFile.findKeyword( "OPERNUM" ); + if ( !opernumKeyword.has_value() ) + { + // Add INCLUDE for OPERNUM.GRDECL in REGIONS section + if ( !deckFile.addIncludeKeyword( "REGIONS", "OPERNUM", "OPERNUM.GRDECL" ) ) + { + return std::unexpected( "Failed to add INCLUDE for OPERNUM in REGIONS section" ); + } + RiaLogging::info( "Added INCLUDE 'OPERNUM.GRDECL' to REGIONS section" ); + } + else + { + RiaLogging::info( "OPERNUM keyword already exists in deck, skipping INCLUDE" ); + } + + // Create OPERATER keyword to multiply pore volume in border region + // OPERATER format: TARGET_ARRAY REGION_NUMBER OPERATION ARRAY_PARAMETER PARAM1 PARAM2 REGION_NAME + // Example: OPERATER / PORV 1 MULTX PORV 1.0e6 1* 1* / + float porvMultiplier = 1.0e6f; // TODO: Make this user-configurable + Opm::DeckKeyword operaterKw = RimKeywordFactory::operaterKeyword( "PORV", operNumRegion, "MULTX", "PORV", porvMultiplier ); + + // Add OPERATER keyword to EDIT section + if ( !deckFile.replaceKeyword( "EDIT", operaterKw ) ) + { + return std::unexpected( "Failed to add OPERATER keyword to EDIT section" ); + } + + RiaLogging::info( QString( "Added OPERATER keyword to multiply PORV in region %1 by %2" ).arg( operNumRegion ).arg( porvMultiplier ) ); } // Remove SKIP keywords that were used as placeholders for filtered-out keywords diff --git a/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.cpp b/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.cpp index 0463d00242..6ea80f9432 100644 --- a/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.cpp +++ b/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.cpp @@ -733,6 +733,9 @@ std::vector RifOpmFlowDeckFile::regdims() dims.push_back( rec.getItem( R::NMFIPR::itemName ).get( 0 ) ); dims.push_back( rec.getItem( R::NRFREG::itemName ).get( 0 ) ); dims.push_back( rec.getItem( R::NTFREG::itemName ).get( 0 ) ); + dims.push_back( rec.getItem( R::MAX_ETRACK::itemName ).get( 0 ) ); + dims.push_back( rec.getItem( R::NTCREG::itemName ).get( 0 ) ); + dims.push_back( rec.getItem( R::MAX_OPERNUM::itemName ).get( 0 ) ); return dims; } @@ -742,7 +745,13 @@ std::vector RifOpmFlowDeckFile::regdims() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -bool RifOpmFlowDeckFile::setRegdims( int maxRegions, int maxRegionDefinitions, int maxRegionFlowConnections, int maxFIPRegions ) +bool RifOpmFlowDeckFile::setRegdims( int maxRegions, + int maxRegionDefinitions, + int maxRegionFlowConnections, + int maxFIPRegions, + int maxEtrack, + int maxCompRegions, + int maxOperNum ) { using R = Opm::ParserKeywords::REGDIMS; if ( m_fileDeck.get() == nullptr ) return false; @@ -755,7 +764,10 @@ bool RifOpmFlowDeckFile::setRegdims( int maxRegions, int maxRegionDefinitions, i newKw.addRecord( Opm::DeckRecord{ { RifOpmDeckTools::item( R::NTFIP::itemName, maxRegions ), RifOpmDeckTools::item( R::NMFIPR::itemName, maxRegionDefinitions ), RifOpmDeckTools::item( R::NRFREG::itemName, maxRegionFlowConnections ), - RifOpmDeckTools::item( R::NTFREG::itemName, maxFIPRegions ) } } ); + RifOpmDeckTools::item( R::NTFREG::itemName, maxFIPRegions ), + RifOpmDeckTools::item( R::MAX_ETRACK::itemName, maxEtrack ), + RifOpmDeckTools::item( R::NTCREG::itemName, maxCompRegions ), + RifOpmDeckTools::item( R::MAX_OPERNUM::itemName, maxOperNum ) } } ); m_fileDeck->erase( idx.value() ); m_fileDeck->insert( idx.value(), newKw ); @@ -800,11 +812,17 @@ bool RifOpmFlowDeckFile::ensureRegdimsKeyword() } // Create REGDIMS keyword with default values: "6* 1 /" + // Items: NTFIP NMFIPR NRFREG NTFREG MAX_ETRACK NTCREG MAX_OPERNUM + // Default values from REGDIMS schema: 1, 1, 0, 0, 0, 1, 0 + // But we want to set MAX_OPERNUM to 1 to reserve one region Opm::DeckKeyword regdimsKw( ( Opm::ParserKeyword( R::keywordName ) ) ); regdimsKw.addRecord( Opm::DeckRecord{ { RifOpmDeckTools::item( R::NTFIP::itemName, 1 ), RifOpmDeckTools::item( R::NMFIPR::itemName, 1 ), - RifOpmDeckTools::item( R::NRFREG::itemName, 1 ), - RifOpmDeckTools::item( R::NTFREG::itemName, 1 ) } } ); + RifOpmDeckTools::item( R::NRFREG::itemName, 0 ), + RifOpmDeckTools::item( R::NTFREG::itemName, 0 ), + RifOpmDeckTools::item( R::MAX_ETRACK::itemName, 0 ), + RifOpmDeckTools::item( R::NTCREG::itemName, 1 ), + RifOpmDeckTools::item( R::MAX_OPERNUM::itemName, 1 ) } } ); m_fileDeck->insert( insertIdx, regdimsKw ); return true; diff --git a/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.h b/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.h index df683785c5..a2bc53a638 100644 --- a/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.h +++ b/ApplicationLibCode/FileInterface/RifOpmFlowDeckFile.h @@ -76,7 +76,13 @@ public: bool setWsegdims( int maxMSWells, int maxSegmentsPerWell, int maxBranchesPerWell ); std::vector regdims(); - bool setRegdims( int maxRegions, int maxRegionDefinitions, int maxRegionFlowConnections, int maxFIPRegions ); + bool setRegdims( int maxRegions, + int maxRegionDefinitions, + int maxRegionFlowConnections, + int maxFIPRegions, + int maxEtrack, + int maxCompRegions, + int maxOperNum ); bool ensureRegdimsKeyword(); bool addIncludeKeyword( std::string section, std::string keyword, std::string filePath ); diff --git a/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.cpp b/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.cpp index 1a98ea90ee..facf762b1a 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.cpp @@ -109,9 +109,9 @@ void generateBorderResult( RimEclipseCase* eclipseCase, cvf::refupdateConnectedEditors(); + + return borderCellValue; } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.h b/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.h index ae2b549fee..0d68993b92 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.h +++ b/ApplicationLibCode/ReservoirDataModel/RigEclipseResultTools.h @@ -47,7 +47,7 @@ void createResultVector( RimEclipseCase& eclipseCase, const QString& resultName, void generateBorderResult( RimEclipseCase* eclipseCase, cvf::ref customVisibility, const QString& resultName = "BORDER" ); -void generateOperNumResult( RimEclipseCase* eclipseCase, int borderCellValue = -1 ); +int generateOperNumResult( RimEclipseCase* eclipseCase, int borderCellValue = -1 ); int findMaxOperNumValue( RimEclipseCase* eclipseCase ); diff --git a/ApplicationLibCode/UnitTests/RifOpmFlowDeckFile-Test.cpp b/ApplicationLibCode/UnitTests/RifOpmFlowDeckFile-Test.cpp index 3f1ab19470..1b50dd2ffb 100644 --- a/ApplicationLibCode/UnitTests/RifOpmFlowDeckFile-Test.cpp +++ b/ApplicationLibCode/UnitTests/RifOpmFlowDeckFile-Test.cpp @@ -36,9 +36,9 @@ TEST( RifOpmFlowDeckFileTest, RegdimsExistingKeyword ) // Test reading existing REGDIMS auto regdimsValues = deckFile.regdims(); EXPECT_FALSE( regdimsValues.empty() ) << "REGDIMS should exist in NORNE file"; - EXPECT_EQ( 4, regdimsValues.size() ) << "REGDIMS should have 4 values"; + EXPECT_EQ( 7, regdimsValues.size() ) << "REGDIMS should have 7 values"; - // Values from the NORNE file: 22 3 1* 20 + // Values from the NORNE file: 22 3 1* 20 (rest are defaults) EXPECT_EQ( 22, regdimsValues[0] ) << "NTFIP should be 22"; EXPECT_EQ( 3, regdimsValues[1] ) << "NMFIPR should be 3"; // Third value is 1* (default), but OPM should handle this @@ -68,10 +68,10 @@ TEST( RifOpmFlowDeckFileTest, RegdimsAddKeyword ) // Verify REGDIMS now exists auto regdimsValues = deckFile.regdims(); EXPECT_FALSE( regdimsValues.empty() ) << "REGDIMS should exist after adding"; - EXPECT_EQ( 4, regdimsValues.size() ) << "REGDIMS should have 4 values"; + EXPECT_EQ( 7, regdimsValues.size() ) << "REGDIMS should have 7 values"; - // Verify default values (6* 1 /) - OPM should interpret 6* as default values followed by 1 - EXPECT_EQ( 1, regdimsValues[3] ) << "NTFREG should be 1 (the explicit value)"; + // Verify default values (6* 1 /) - items 1-6 are defaults, item 7 (MAX_OPERNUM) is 1 + EXPECT_EQ( 1, regdimsValues[6] ) << "MAX_OPERNUM should be 1 (the explicit value)"; // Test that calling ensureRegdimsKeyword again doesn't fail bool addAgainSuccess = deckFile.ensureRegdimsKeyword(); @@ -94,19 +94,22 @@ TEST( RifOpmFlowDeckFileTest, RegdimsSetValues ) bool addSuccess = deckFile.ensureRegdimsKeyword(); EXPECT_TRUE( addSuccess ) << "Should successfully add REGDIMS keyword"; - // Set custom REGDIMS values - bool setSuccess = deckFile.setRegdims( 10, 5, 3, 8 ); + // Set custom REGDIMS values (NTFIP NMFIPR NRFREG NTFREG MAX_ETRACK NTCREG MAX_OPERNUM) + bool setSuccess = deckFile.setRegdims( 10, 5, 3, 8, 0, 2, 4 ); EXPECT_TRUE( setSuccess ) << "Should successfully set REGDIMS values"; // Verify the values were set correctly auto regdimsValues = deckFile.regdims(); EXPECT_FALSE( regdimsValues.empty() ) << "REGDIMS should exist"; - EXPECT_EQ( 4, regdimsValues.size() ) << "REGDIMS should have 4 values"; + EXPECT_EQ( 7, regdimsValues.size() ) << "REGDIMS should have 7 values"; EXPECT_EQ( 10, regdimsValues[0] ) << "NTFIP should be 10"; EXPECT_EQ( 5, regdimsValues[1] ) << "NMFIPR should be 5"; EXPECT_EQ( 3, regdimsValues[2] ) << "NRFREG should be 3"; EXPECT_EQ( 8, regdimsValues[3] ) << "NTFREG should be 8"; + EXPECT_EQ( 0, regdimsValues[4] ) << "MAX_ETRACK should be 0"; + EXPECT_EQ( 2, regdimsValues[5] ) << "NTCREG should be 2"; + EXPECT_EQ( 4, regdimsValues[6] ) << "MAX_OPERNUM should be 4"; } //-------------------------------------------------------------------------------------------------- @@ -141,7 +144,7 @@ TEST( RifOpmFlowDeckFileTest, RegdimsSaveAndReload ) // Verify REGDIMS exists in reloaded deck auto reloadedRegdimsValues = reloadedDeckFile.regdims(); EXPECT_FALSE( reloadedRegdimsValues.empty() ) << "REGDIMS should exist in reloaded deck"; - EXPECT_EQ( 4, reloadedRegdimsValues.size() ) << "REGDIMS should have 4 values in reloaded deck"; + EXPECT_EQ( 7, reloadedRegdimsValues.size() ) << "REGDIMS should have 7 values in reloaded deck"; } //--------------------------------------------------------------------------------------------------