mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-26 07:00:17 -05:00
#13333 Simulation Input: Add EDITNNC export functionality for sector models
This commit is contained in:
1 parent
e681b4603a
commit
1c9c5a85ad
5 files changed
+491
-16
No files matched your search
@@ -29,6 +29,7 @@
|
||||
#include "RigEclipseResultTools.h"
|
||||
#include "RigFault.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigSimulationInputTool.h"
|
||||
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimWellPath.h"
|
||||
@@ -42,7 +43,9 @@
|
||||
#include "opm/input/eclipse/Deck/DeckRecord.hpp"
|
||||
#include "opm/input/eclipse/Parser/ParserKeywords/B.hpp"
|
||||
#include "opm/input/eclipse/Parser/ParserKeywords/C.hpp"
|
||||
#include "opm/input/eclipse/Parser/ParserKeywords/E.hpp"
|
||||
#include "opm/input/eclipse/Parser/ParserKeywords/F.hpp"
|
||||
#include "opm/input/eclipse/Parser/ParserKeywords/N.hpp"
|
||||
#include "opm/input/eclipse/Parser/ParserKeywords/O.hpp"
|
||||
#include "opm/input/eclipse/Parser/ParserKeywords/W.hpp"
|
||||
|
||||
@@ -656,4 +659,32 @@ Opm::DeckKeyword operaterKeyword( std::string targetProperty,
|
||||
return operaterKw;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
Opm::DeckKeyword editnncKeyword( const std::vector<RigSimulationInputTool::TransformedNNCConnection>& connections )
|
||||
{
|
||||
using E = Opm::ParserKeywords::EDITNNC;
|
||||
|
||||
Opm::DeckKeyword kw{ Opm::ParserKeywords::EDITNNC{} };
|
||||
|
||||
for ( const auto& conn : connections )
|
||||
{
|
||||
std::vector<Opm::DeckItem> items;
|
||||
|
||||
// Convert from 0-based sector coordinates to 1-based Eclipse convention
|
||||
items.push_back( RifOpmDeckTools::item( E::I1::itemName, static_cast<int>( conn.cell1.i() + 1 ) ) );
|
||||
items.push_back( RifOpmDeckTools::item( E::J1::itemName, static_cast<int>( conn.cell1.j() + 1 ) ) );
|
||||
items.push_back( RifOpmDeckTools::item( E::K1::itemName, static_cast<int>( conn.cell1.k() + 1 ) ) );
|
||||
items.push_back( RifOpmDeckTools::item( E::I2::itemName, static_cast<int>( conn.cell2.i() + 1 ) ) );
|
||||
items.push_back( RifOpmDeckTools::item( E::J2::itemName, static_cast<int>( conn.cell2.j() + 1 ) ) );
|
||||
items.push_back( RifOpmDeckTools::item( E::K2::itemName, static_cast<int>( conn.cell2.k() + 1 ) ) );
|
||||
items.push_back( RifOpmDeckTools::item( E::TRAN_MULT::itemName, conn.transmissibility ) );
|
||||
|
||||
kw.addRecord( Opm::DeckRecord{ std::move( items ) } );
|
||||
}
|
||||
|
||||
return kw;
|
||||
}
|
||||
|
||||
} // namespace RimKeywordFactory
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ReservoirDataModel/SimulationFile/RigSimulationInputTool.h"
|
||||
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <optional>
|
||||
@@ -71,5 +73,6 @@ Opm::DeckKeyword operaterKeyword( std::string targetProperty,
|
||||
std::string inputProperty,
|
||||
std::optional<float> alpha = std::nullopt,
|
||||
std::optional<float> beta = std::nullopt );
|
||||
Opm::DeckKeyword editnncKeyword( const std::vector<RigSimulationInputTool::TransformedNNCConnection>& connections );
|
||||
|
||||
} // namespace RimKeywordFactory
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "RigSimulationInputTool.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
#include "RiaNncDefines.h"
|
||||
|
||||
#include "RifEclipseInputFileTools.h"
|
||||
#include "RifOpmDeckTools.h"
|
||||
@@ -31,6 +32,7 @@
|
||||
#include "RigEclipseCaseData.h"
|
||||
#include "RigEclipseResultTools.h"
|
||||
#include "RigGridExportAdapter.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigResdataGridConverter.h"
|
||||
#include "RigSimulationInputSettings.h"
|
||||
#include "Well/RigSimWellData.h"
|
||||
@@ -145,6 +147,11 @@ std::expected<void, QString> RigSimulationInputTool::exportSimulationInput( RimE
|
||||
return result;
|
||||
}
|
||||
|
||||
if ( auto result = exportEditNncKeyword( &eclipseCase, settings, deckFile ); !result )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// Remove SKIP keywords that were used as placeholders for filtered-out keywords
|
||||
deckFile.removeKeywords( "SKIP" );
|
||||
|
||||
@@ -865,8 +872,8 @@ std::expected<RigBoundingBoxIjk<caf::VecIjk0>, QString>
|
||||
}
|
||||
|
||||
// Get the clamped coordinates from the intersection
|
||||
cvf::Vec3st clampedMin = intersection->min();
|
||||
cvf::Vec3st clampedMax = intersection->max();
|
||||
caf::VecIjk0 clampedMin = intersection->min();
|
||||
caf::VecIjk0 clampedMax = intersection->max();
|
||||
|
||||
// Log if clamping occurred (partial overlap)
|
||||
const auto& origMin = inputBox.min();
|
||||
@@ -893,20 +900,8 @@ std::expected<RigBoundingBoxIjk<caf::VecIjk0>, QString>
|
||||
}
|
||||
|
||||
// Transform clamped coordinates to sector-relative coordinates
|
||||
auto transformResult1 =
|
||||
RigGridExportAdapter::transformIjkToSectorCoordinates( caf::VecIjk0( clampedMin.x(), clampedMin.y(), clampedMin.z() ),
|
||||
sectorMin,
|
||||
sectorMax,
|
||||
refinement,
|
||||
false,
|
||||
false );
|
||||
auto transformResult2 =
|
||||
RigGridExportAdapter::transformIjkToSectorCoordinates( caf::VecIjk0( clampedMax.x(), clampedMax.y(), clampedMax.z() ),
|
||||
sectorMin,
|
||||
sectorMax,
|
||||
refinement,
|
||||
false,
|
||||
true );
|
||||
auto transformResult1 = RigGridExportAdapter::transformIjkToSectorCoordinates( clampedMin, sectorMin, sectorMax, refinement, false, false );
|
||||
auto transformResult2 = RigGridExportAdapter::transformIjkToSectorCoordinates( clampedMax, sectorMin, sectorMax, refinement, false, true );
|
||||
|
||||
if ( !transformResult1 )
|
||||
{
|
||||
@@ -1547,3 +1542,253 @@ std::expected<void, QString> RigSimulationInputTool::addOperNumRegionAndOperater
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RigSimulationInputTool::NNCConnection> RigSimulationInputTool::extractDeckEditNncConnections( RifOpmFlowDeckFile& deckFile,
|
||||
const RigMainGrid& mainGrid )
|
||||
{
|
||||
std::vector<NNCConnection> connections;
|
||||
|
||||
// Find all EDITNNC keywords in deck (there may be multiple)
|
||||
auto keywords = deckFile.findAllKeywordsWithIndices( "EDITNNC" );
|
||||
|
||||
for ( const auto& keywordPair : keywords )
|
||||
{
|
||||
const auto& keyword = keywordPair.second;
|
||||
|
||||
// Each keyword can have multiple records
|
||||
for ( size_t recordIdx = 0; recordIdx < keyword.size(); ++recordIdx )
|
||||
{
|
||||
const auto& record = keyword.getRecord( recordIdx );
|
||||
|
||||
// Read IJK coordinates directly from deck (1-based) and convert to 0-based
|
||||
caf::VecIjk1 ijk1( record.getItem( 0 ).get<int>( 0 ), record.getItem( 1 ).get<int>( 0 ), record.getItem( 2 ).get<int>( 0 ) );
|
||||
caf::VecIjk1 ijk2( record.getItem( 3 ).get<int>( 0 ), record.getItem( 4 ).get<int>( 0 ), record.getItem( 5 ).get<int>( 0 ) );
|
||||
double transMult = record.getItem( 6 ).get<double>( 0 );
|
||||
|
||||
// Convert IJK to global cell indices
|
||||
size_t c1Idx = mainGrid.cellIndexFromIJK( ijk1.toZeroBased() );
|
||||
size_t c2Idx = mainGrid.cellIndexFromIJK( ijk2.toZeroBased() );
|
||||
|
||||
// Validate indices
|
||||
if ( c1Idx == cvf::UNDEFINED_SIZE_T || c2Idx == cvf::UNDEFINED_SIZE_T )
|
||||
{
|
||||
RiaLogging::warning( QString( "Invalid EDITNNC connection in deck: (%1)-(%2)" )
|
||||
.arg( QString::fromStdString( ijk1.toString() ) )
|
||||
.arg( QString::fromStdString( ijk2.toString() ) ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
connections.push_back( { c1Idx, c2Idx, transMult } );
|
||||
}
|
||||
}
|
||||
|
||||
return connections;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RigSimulationInputTool::NNCConnection>
|
||||
RigSimulationInputTool::filterInternalSectorConnections( const std::vector<NNCConnection>& allConnections,
|
||||
const RigMainGrid& mainGrid,
|
||||
const caf::VecIjk0& min,
|
||||
const caf::VecIjk0& max )
|
||||
{
|
||||
std::vector<NNCConnection> filteredConnections;
|
||||
|
||||
RigBoundingBoxIjk sectorBounds( min, max );
|
||||
|
||||
for ( const auto& conn : allConnections )
|
||||
{
|
||||
// Get IJK for both cells
|
||||
auto ijk1 = mainGrid.ijkFromCellIndex( conn.c1GlobIdx );
|
||||
auto ijk2 = mainGrid.ijkFromCellIndex( conn.c2GlobIdx );
|
||||
|
||||
// Only keep if BOTH cells are inside sector bounds
|
||||
if ( ijk1 && ijk2 && sectorBounds.contains( *ijk1 ) && sectorBounds.contains( *ijk2 ) )
|
||||
{
|
||||
filteredConnections.push_back( conn );
|
||||
}
|
||||
}
|
||||
|
||||
return filteredConnections;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
caf::VecIjk0 RigSimulationInputTool::transformToSectorCoordinates( const caf::VecIjk0& globalIjk,
|
||||
const caf::VecIjk0& min,
|
||||
const cvf::Vec3st& refinement )
|
||||
{
|
||||
// Transform global IJK to sector-relative coordinates with refinement
|
||||
// Formula matches RigGridExportAdapter::transformIjkToSectorCoordinates for box minimum coordinates
|
||||
// Returns 0-based sector coordinates
|
||||
return caf::VecIjk0( ( globalIjk.i() - min.i() ) * refinement.x(),
|
||||
( globalIjk.j() - min.j() ) * refinement.y(),
|
||||
( globalIjk.k() - min.k() ) * refinement.z() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::expected<RigSimulationInputTool::TransformedNNCConnection, QString>
|
||||
RigSimulationInputTool::transformNNCToSectorCoordinates( const NNCConnection& connection,
|
||||
const RigMainGrid& mainGrid,
|
||||
const caf::VecIjk0& min,
|
||||
const cvf::Vec3st& refinement )
|
||||
{
|
||||
// Get global IJK for both cells
|
||||
auto gijk1 = mainGrid.ijkFromCellIndex( connection.c1GlobIdx );
|
||||
if ( !gijk1 )
|
||||
{
|
||||
return std::unexpected( QString( "Invalid cell index: %1" ).arg( connection.c1GlobIdx ) );
|
||||
}
|
||||
|
||||
auto gijk2 = mainGrid.ijkFromCellIndex( connection.c2GlobIdx );
|
||||
if ( !gijk2 )
|
||||
{
|
||||
return std::unexpected( QString( "Invalid cell index: %1" ).arg( connection.c2GlobIdx ) );
|
||||
}
|
||||
|
||||
// Transform to sector coordinates using utility method
|
||||
TransformedNNCConnection transformed;
|
||||
transformed.cell1 = transformToSectorCoordinates( *gijk1, min, refinement );
|
||||
transformed.cell2 = transformToSectorCoordinates( *gijk2, min, refinement );
|
||||
transformed.transmissibility = connection.transmissibility;
|
||||
|
||||
return transformed;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RigSimulationInputTool::TransformedNNCConnection> RigSimulationInputTool::refineEditNncConnection( const NNCConnection& connection,
|
||||
const RigMainGrid& mainGrid,
|
||||
const caf::VecIjk0& min,
|
||||
const cvf::Vec3st& refinement )
|
||||
{
|
||||
// Get global IJK for both cells
|
||||
auto gijk1 = mainGrid.ijkFromCellIndex( connection.c1GlobIdx );
|
||||
auto gijk2 = mainGrid.ijkFromCellIndex( connection.c2GlobIdx );
|
||||
|
||||
if ( !gijk1 || !gijk2 )
|
||||
{
|
||||
return {}; // Return empty on error
|
||||
}
|
||||
|
||||
// For EDITNNC: Connect subcells at same relative positions within coarse cells
|
||||
// Example: refinement (2,2,1) creates 4 connections:
|
||||
// (0,0,0) → (0,0,0)
|
||||
// (0,1,0) → (0,1,0)
|
||||
// (1,0,0) → (1,0,0)
|
||||
// (1,1,0) → (1,1,0)
|
||||
// Each connection gets the SAME TRANSMUL (no division)
|
||||
|
||||
// Get base sector coordinates for both cells (start of refined block)
|
||||
caf::VecIjk0 baseCell1 = transformToSectorCoordinates( *gijk1, min, refinement );
|
||||
caf::VecIjk0 baseCell2 = transformToSectorCoordinates( *gijk2, min, refinement );
|
||||
|
||||
std::vector<TransformedNNCConnection> refined;
|
||||
for ( size_t subI = 0; subI < refinement.x(); ++subI )
|
||||
{
|
||||
for ( size_t subJ = 0; subJ < refinement.y(); ++subJ )
|
||||
{
|
||||
for ( size_t subK = 0; subK < refinement.z(); ++subK )
|
||||
{
|
||||
// Add subcell offset to base coordinates
|
||||
TransformedNNCConnection conn;
|
||||
conn.cell1 = caf::VecIjk0( baseCell1.i() + subI, baseCell1.j() + subJ, baseCell1.k() + subK );
|
||||
|
||||
// SAME relative position in cell2
|
||||
conn.cell2 = caf::VecIjk0( baseCell2.i() + subI, baseCell2.j() + subJ, baseCell2.k() + subK );
|
||||
|
||||
conn.transmissibility = connection.transmissibility; // Same TRAN_MULT for all
|
||||
refined.push_back( conn );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return refined;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::expected<void, QString> RigSimulationInputTool::exportEditNncKeyword( RimEclipseCase* eclipseCase,
|
||||
const RigSimulationInputSettings& settings,
|
||||
RifOpmFlowDeckFile& deckFile )
|
||||
{
|
||||
RigMainGrid* mainGrid = eclipseCase->eclipseCaseData()->mainGrid();
|
||||
if ( !mainGrid ) return std::unexpected( "No main grid available" );
|
||||
|
||||
// Step 1: Extract EDITNNC from deck only
|
||||
auto allConnections = extractDeckEditNncConnections( deckFile, *mainGrid );
|
||||
|
||||
if ( allConnections.empty() )
|
||||
{
|
||||
RiaLogging::info( "No EDITNNC keywords found in deck - skipping EDITNNC export" );
|
||||
return {};
|
||||
}
|
||||
|
||||
// Step 2: Filter for internal sector
|
||||
auto sectorConnections = filterInternalSectorConnections( allConnections, *mainGrid, settings.min(), settings.max() );
|
||||
|
||||
if ( sectorConnections.empty() )
|
||||
{
|
||||
RiaLogging::info( "No internal EDITNNC connections in sector - skipping EDITNNC export" );
|
||||
return {};
|
||||
}
|
||||
|
||||
RiaLogging::info(
|
||||
QString( "Found %1 internal EDITNNC connections in sector (out of %2 total)" ).arg( sectorConnections.size() ).arg( allConnections.size() ) );
|
||||
|
||||
// Step 3: Transform and refine
|
||||
bool hasRefinement = ( settings.refinement().x() != 1 || settings.refinement().y() != 1 || settings.refinement().z() != 1 );
|
||||
|
||||
std::vector<TransformedNNCConnection> transformedConnections;
|
||||
|
||||
if ( hasRefinement )
|
||||
{
|
||||
// Refine to subcells with corresponding positions
|
||||
for ( const auto& conn : sectorConnections )
|
||||
{
|
||||
auto refined = refineEditNncConnection( conn, *mainGrid, settings.min(), settings.refinement() );
|
||||
transformedConnections.insert( transformedConnections.end(), refined.begin(), refined.end() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Simple coordinate transformation, no refinement
|
||||
for ( const auto& conn : sectorConnections )
|
||||
{
|
||||
auto result = transformNNCToSectorCoordinates( conn, *mainGrid, settings.min(), settings.refinement() );
|
||||
if ( result )
|
||||
{
|
||||
transformedConnections.push_back( *result );
|
||||
}
|
||||
else
|
||||
{
|
||||
RiaLogging::warning( result.error() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( transformedConnections.empty() )
|
||||
{
|
||||
return std::unexpected( "Failed to transform any EDITNNC connections" );
|
||||
}
|
||||
|
||||
RiaLogging::info( QString( "Exporting %1 EDITNNC connections to sector model" ).arg( transformedConnections.size() ) );
|
||||
|
||||
// Step 4: Create keyword
|
||||
auto editnncKw = RimKeywordFactory::editnncKeyword( transformedConnections );
|
||||
|
||||
// Step 5: Replace/add to EDIT section (not GRID!)
|
||||
deckFile.replaceKeyword( "EDIT", editnncKw );
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -37,6 +37,7 @@ class RigSimWellData;
|
||||
template <typename Vec>
|
||||
class RigBoundingBoxIjk;
|
||||
class RigGridExportAdapter;
|
||||
class RigMainGrid;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
@@ -52,6 +53,24 @@ class DeckKeyword;
|
||||
class RigSimulationInputTool
|
||||
{
|
||||
public:
|
||||
// Data structures for NNC export
|
||||
|
||||
/// Raw NNC connection with global cell indices
|
||||
struct NNCConnection
|
||||
{
|
||||
size_t c1GlobIdx; // Global cell index for cell 1
|
||||
size_t c2GlobIdx; // Global cell index for cell 2
|
||||
double transmissibility; // Transmissibility value
|
||||
};
|
||||
|
||||
/// Transformed NNC connection with sector-relative 0-based IJK coordinates
|
||||
struct TransformedNNCConnection
|
||||
{
|
||||
caf::VecIjk0 cell1 = caf::VecIjk0::ZERO; // 0-based sector IJK for cell 1
|
||||
caf::VecIjk0 cell2 = caf::VecIjk0::ZERO; // 0-based sector IJK for cell 2
|
||||
double transmissibility = 0.0; // Transmissibility value
|
||||
};
|
||||
|
||||
static std::expected<void, QString>
|
||||
exportSimulationInput( RimEclipseCase& eclipseCase, const RigSimulationInputSettings& settings, cvf::UByteArray* visibility );
|
||||
|
||||
@@ -75,6 +94,21 @@ public:
|
||||
|
||||
static std::vector<int> createRefinedVisibility( const RigGridExportAdapter& gridAdapter );
|
||||
|
||||
// Public EDITNNC methods for testing
|
||||
static std::vector<NNCConnection> extractDeckEditNncConnections( RifOpmFlowDeckFile& deckFile, const RigMainGrid& mainGrid );
|
||||
static std::vector<NNCConnection> filterInternalSectorConnections( const std::vector<NNCConnection>& allConnections,
|
||||
const RigMainGrid& mainGrid,
|
||||
const caf::VecIjk0& min,
|
||||
const caf::VecIjk0& max );
|
||||
static std::expected<TransformedNNCConnection, QString> transformNNCToSectorCoordinates( const NNCConnection& connection,
|
||||
const RigMainGrid& mainGrid,
|
||||
const caf::VecIjk0& min,
|
||||
const cvf::Vec3st& refinement );
|
||||
static std::vector<TransformedNNCConnection> refineEditNncConnection( const NNCConnection& connection,
|
||||
const RigMainGrid& mainGrid,
|
||||
const caf::VecIjk0& min,
|
||||
const cvf::Vec3st& refinement );
|
||||
|
||||
private:
|
||||
static std::expected<void, QString> updateCornerPointGridInDeckFile( RimEclipseCase* eclipseCase,
|
||||
const RigSimulationInputSettings& settings,
|
||||
@@ -116,6 +150,12 @@ private:
|
||||
static std::expected<void, QString>
|
||||
filterAndUpdateWellKeywords( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );
|
||||
|
||||
static std::expected<void, QString>
|
||||
exportEditNncKeyword( RimEclipseCase* eclipseCase, const RigSimulationInputSettings& settings, RifOpmFlowDeckFile& deckFile );
|
||||
|
||||
// Utility: Transform global IJK to sector-relative coordinates with refinement
|
||||
static caf::VecIjk0 transformToSectorCoordinates( const caf::VecIjk0& globalIjk, const caf::VecIjk0& min, const cvf::Vec3st& refinement );
|
||||
|
||||
static std::expected<void, QString> addOperNumRegionAndOperater( RifOpmFlowDeckFile& deckFile,
|
||||
const RigGridExportAdapter& gridAdapter,
|
||||
const std::vector<int>& operNumResult,
|
||||
|
||||
@@ -1150,3 +1150,159 @@ TEST( RigSimulationInputTool, ExportModel5WithRefinement_3_5_1 )
|
||||
// K1: (1-0)*1 + 1 = 1, K2: (6-0)*1 + 1 = 6
|
||||
EXPECT_TRUE( checkCompdatCoordinates( "B-2H", 1, 6 ) ) << "B-2H COMPDAT K coordinates not centered correctly";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Helper to load model5 test case
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
static cvf::ref<RigEclipseCaseData> loadModel5TestCase()
|
||||
{
|
||||
QDir baseFolder( TEST_DATA_DIR );
|
||||
bool subFolderExists = baseFolder.cd( "RigSimulationInputTool/model5" );
|
||||
if ( !subFolderExists ) return nullptr;
|
||||
|
||||
QString egridFilename( "0_BASE_MODEL5.EGRID" );
|
||||
QString egridFilePath = baseFolder.absoluteFilePath( egridFilename );
|
||||
if ( !QFile::exists( egridFilePath ) ) return nullptr;
|
||||
|
||||
std::unique_ptr<RimEclipseResultCase> resultCase( new RimEclipseResultCase );
|
||||
cvf::ref<RigEclipseCaseData> caseData = new RigEclipseCaseData( resultCase.get() );
|
||||
|
||||
cvf::ref<RifReaderEclipseOutput> reader = new RifReaderEclipseOutput;
|
||||
bool loadResult = reader->open( egridFilePath, caseData.p() );
|
||||
if ( !loadResult ) return nullptr;
|
||||
|
||||
return caseData;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Test filterInternalSectorConnections - both cells inside
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigSimulationInputTool, FilterNNCConnections_BothInside )
|
||||
{
|
||||
// Load model5 test data (20x30x10 grid)
|
||||
auto caseData = loadModel5TestCase();
|
||||
ASSERT_TRUE( caseData.notNull() );
|
||||
ASSERT_TRUE( caseData->mainGrid() != nullptr );
|
||||
EXPECT_EQ( 20u, caseData->mainGrid()->cellCountI() );
|
||||
EXPECT_EQ( 30u, caseData->mainGrid()->cellCountJ() );
|
||||
EXPECT_EQ( 10u, caseData->mainGrid()->cellCountK() );
|
||||
|
||||
RigMainGrid* mainGrid = caseData->mainGrid();
|
||||
|
||||
// Create test connections using the real grid
|
||||
std::vector<RigSimulationInputTool::NNCConnection> allConnections;
|
||||
|
||||
// Connection 1: (2,3,4) to (3,3,4) - both inside sector [1,5] x [1,5] x [1,5]
|
||||
size_t c1Idx = mainGrid->cellIndexFromIJK( 2, 3, 4 );
|
||||
size_t c2Idx = mainGrid->cellIndexFromIJK( 3, 3, 4 );
|
||||
allConnections.push_back( { c1Idx, c2Idx, 1.5 } );
|
||||
|
||||
// Connection 2: (10,20,8) to (11,20,8) - both outside sector
|
||||
c1Idx = mainGrid->cellIndexFromIJK( 10, 20, 8 );
|
||||
c2Idx = mainGrid->cellIndexFromIJK( 11, 20, 8 );
|
||||
allConnections.push_back( { c1Idx, c2Idx, 2.5 } );
|
||||
|
||||
// Connection 3: (1,2,3) to (15,15,8) - one inside, one outside
|
||||
c1Idx = mainGrid->cellIndexFromIJK( 1, 2, 3 );
|
||||
c2Idx = mainGrid->cellIndexFromIJK( 15, 15, 8 );
|
||||
allConnections.push_back( { c1Idx, c2Idx, 3.5 } );
|
||||
|
||||
// Filter for sector [1,5] x [1,5] x [1,5]
|
||||
caf::VecIjk0 sectorMin( 1, 1, 1 );
|
||||
caf::VecIjk0 sectorMax( 5, 5, 5 );
|
||||
|
||||
auto filtered = RigSimulationInputTool::filterInternalSectorConnections( allConnections, *mainGrid, sectorMin, sectorMax );
|
||||
|
||||
// Only connection 1 should remain
|
||||
ASSERT_EQ( 1u, filtered.size() );
|
||||
EXPECT_EQ( 1.5, filtered[0].transmissibility );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Test transformNNCToSectorCoordinates - no refinement
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigSimulationInputTool, TransformNNCToSectorCoordinates_NoRefinement )
|
||||
{
|
||||
// Load model5 test data (20x30x10 grid)
|
||||
auto caseData = loadModel5TestCase();
|
||||
ASSERT_TRUE( caseData.notNull() );
|
||||
RigMainGrid* mainGrid = caseData->mainGrid();
|
||||
|
||||
// Connection between (10,15,5) and (11,15,5) - I-face neighbors
|
||||
size_t c1Idx = mainGrid->cellIndexFromIJK( 10, 15, 5 );
|
||||
size_t c2Idx = mainGrid->cellIndexFromIJK( 11, 15, 5 );
|
||||
|
||||
RigSimulationInputTool::NNCConnection connection{ c1Idx, c2Idx, 4.5 };
|
||||
|
||||
// Sector starts at (5,10,2)
|
||||
caf::VecIjk0 sectorMin( 5, 10, 2 );
|
||||
cvf::Vec3st refinement( 1, 1, 1 );
|
||||
|
||||
auto result = RigSimulationInputTool::transformNNCToSectorCoordinates( connection, *mainGrid, sectorMin, refinement );
|
||||
|
||||
ASSERT_TRUE( result.has_value() );
|
||||
|
||||
// Cell 1: (10,15,5) - sector min (5,10,2) = (5,5,3) in 0-based sector coords
|
||||
EXPECT_EQ( 5, result->cell1.i() );
|
||||
EXPECT_EQ( 5, result->cell1.j() );
|
||||
EXPECT_EQ( 3, result->cell1.k() );
|
||||
|
||||
// Cell 2: (11,15,5) - sector min (5,10,2) = (6,5,3) in 0-based sector coords
|
||||
EXPECT_EQ( 6, result->cell2.i() );
|
||||
EXPECT_EQ( 5, result->cell2.j() );
|
||||
EXPECT_EQ( 3, result->cell2.k() );
|
||||
|
||||
EXPECT_EQ( 4.5, result->transmissibility );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Test refineEDITNNCConnection - corresponding subcells with refinement (2,2,1)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigSimulationInputTool, RefineEDITNNCConnection_CorrespondingSubcells )
|
||||
{
|
||||
// Load model5 test data (20x30x10 grid)
|
||||
auto caseData = loadModel5TestCase();
|
||||
ASSERT_TRUE( caseData.notNull() );
|
||||
RigMainGrid* mainGrid = caseData->mainGrid();
|
||||
|
||||
// Connection between cells (3,4,5) and (6,8,7) - non-neighbors
|
||||
size_t c1Idx = mainGrid->cellIndexFromIJK( 3, 4, 5 );
|
||||
size_t c2Idx = mainGrid->cellIndexFromIJK( 6, 8, 7 );
|
||||
|
||||
RigSimulationInputTool::NNCConnection connection{ c1Idx, c2Idx, 2.5 }; // TRAN_MULT = 2.5
|
||||
|
||||
caf::VecIjk0 sectorMin( 0, 0, 0 );
|
||||
cvf::Vec3st refinement( 2, 2, 1 ); // Refine by 2x2x1
|
||||
|
||||
auto refined = RigSimulationInputTool::refineEditNncConnection( connection, *mainGrid, sectorMin, refinement );
|
||||
|
||||
// For EDITNNC: creates connections between corresponding subcells
|
||||
// Should create 2*2*1 = 4 connections (one per subcell pair at same relative position)
|
||||
ASSERT_EQ( 4u, refined.size() );
|
||||
|
||||
// All connections should have same TRAN_MULT (no distribution)
|
||||
for ( const auto& conn : refined )
|
||||
{
|
||||
EXPECT_DOUBLE_EQ( 2.5, conn.transmissibility );
|
||||
}
|
||||
|
||||
// Verify corresponding subcell connections
|
||||
// Cell1 starts at sector coordinates (3*2, 4*2, 5*1) = (6, 8, 5)
|
||||
// Cell2 starts at sector coordinates (6*2, 8*2, 7*1) = (12, 16, 7)
|
||||
|
||||
// Connection 0: (0,0,0) -> (0,0,0): (6,8,5) -> (12,16,7)
|
||||
EXPECT_EQ( caf::VecIjk0( 6, 8, 5 ), refined[0].cell1 );
|
||||
EXPECT_EQ( caf::VecIjk0( 12, 16, 7 ), refined[0].cell2 );
|
||||
|
||||
// Connection 1: (0,1,0) -> (0,1,0): (6,9,5) -> (12,17,7)
|
||||
EXPECT_EQ( caf::VecIjk0( 6, 9, 5 ), refined[1].cell1 );
|
||||
EXPECT_EQ( caf::VecIjk0( 12, 17, 7 ), refined[1].cell2 );
|
||||
|
||||
// Connection 2: (1,0,0) -> (1,0,0): (7,8,5) -> (13,16,7)
|
||||
EXPECT_EQ( caf::VecIjk0( 7, 8, 5 ), refined[2].cell1 );
|
||||
EXPECT_EQ( caf::VecIjk0( 13, 16, 7 ), refined[2].cell2 );
|
||||
|
||||
// Connection 3: (1,1,0) -> (1,1,0): (7,9,5) -> (13,17,7)
|
||||
EXPECT_EQ( caf::VecIjk0( 7, 9, 5 ), refined[3].cell1 );
|
||||
EXPECT_EQ( caf::VecIjk0( 13, 17, 7 ), refined[3].cell2 );
|
||||
}
|
||||
Reference in new issue
Block a user