diff --git a/ApplicationLibCode/ProjectDataModel/RimCornerPointCase.cpp b/ApplicationLibCode/ProjectDataModel/RimCornerPointCase.cpp index 9adf8ea149..b6fd7a3be3 100644 --- a/ApplicationLibCode/ProjectDataModel/RimCornerPointCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimCornerPointCase.cpp @@ -380,8 +380,7 @@ void RimCornerPointCase::computeDepthRelatedResults( RimCornerPointCase& cornerP { if ( RiaPreferencesGrid::current()->autoComputeDepthRelatedProperties() ) { - cornerPointCase.results( RiaDefines::PorosityModelType::MATRIX_MODEL )->computeDepthRelatedResults(); - cornerPointCase.results( RiaDefines::PorosityModelType::FRACTURE_MODEL )->computeDepthRelatedResults(); + cornerPointCase.eclipseCaseData()->computeDepthRelatedResults(); } } diff --git a/ApplicationLibCode/ProjectDataModel/RimEclipseInputCase.cpp b/ApplicationLibCode/ProjectDataModel/RimEclipseInputCase.cpp index 0fea20f5cf..64b760cd2b 100644 --- a/ApplicationLibCode/ProjectDataModel/RimEclipseInputCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimEclipseInputCase.cpp @@ -226,8 +226,7 @@ bool RimEclipseInputCase::openEclipseGridFile() if ( RiaPreferencesGrid::current()->autoComputeDepthRelatedProperties() ) { - results( RiaDefines::PorosityModelType::MATRIX_MODEL )->computeDepthRelatedResults(); - results( RiaDefines::PorosityModelType::FRACTURE_MODEL )->computeDepthRelatedResults(); + eclipseCaseData()->computeDepthRelatedResults(); } results( RiaDefines::PorosityModelType::MATRIX_MODEL )->computeCellVolumes(); diff --git a/ApplicationLibCode/ProjectDataModel/RimEclipseResultCase.cpp b/ApplicationLibCode/ProjectDataModel/RimEclipseResultCase.cpp index 00839ade07..04e19baa66 100644 --- a/ApplicationLibCode/ProjectDataModel/RimEclipseResultCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimEclipseResultCase.cpp @@ -313,8 +313,7 @@ bool RimEclipseResultCase::importGridAndResultMetaData( bool showTimeStepFilter if ( RiaPreferencesGrid::current()->autoComputeDepthRelatedProperties() ) { - results( RiaDefines::PorosityModelType::MATRIX_MODEL )->computeDepthRelatedResults(); - results( RiaDefines::PorosityModelType::FRACTURE_MODEL )->computeDepthRelatedResults(); + eclipseCaseData()->computeDepthRelatedResults(); } results( RiaDefines::PorosityModelType::MATRIX_MODEL )->computeCellVolumes(); diff --git a/ApplicationLibCode/ProjectDataModel/RimEmCase.cpp b/ApplicationLibCode/ProjectDataModel/RimEmCase.cpp index 2faa083743..52522db41d 100644 --- a/ApplicationLibCode/ProjectDataModel/RimEmCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimEmCase.cpp @@ -97,8 +97,7 @@ bool RimEmCase::openEclipseGridFile() if ( RiaPreferencesGrid::current()->autoComputeDepthRelatedProperties() ) { - results( RiaDefines::PorosityModelType::MATRIX_MODEL )->computeDepthRelatedResults(); - results( RiaDefines::PorosityModelType::FRACTURE_MODEL )->computeDepthRelatedResults(); + eclipseCaseData()->computeDepthRelatedResults(); } results( RiaDefines::PorosityModelType::MATRIX_MODEL )->computeCellVolumes(); diff --git a/ApplicationLibCode/ProjectDataModel/RimRoffCase.cpp b/ApplicationLibCode/ProjectDataModel/RimRoffCase.cpp index 8225d5fbfb..8a726bdd00 100644 --- a/ApplicationLibCode/ProjectDataModel/RimRoffCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimRoffCase.cpp @@ -99,8 +99,7 @@ bool RimRoffCase::openEclipseGridFile() if ( RiaPreferencesGrid::current()->autoComputeDepthRelatedProperties() ) { - results( RiaDefines::PorosityModelType::MATRIX_MODEL )->computeDepthRelatedResults(); - results( RiaDefines::PorosityModelType::FRACTURE_MODEL )->computeDepthRelatedResults(); + eclipseCaseData()->computeDepthRelatedResults(); } results( RiaDefines::PorosityModelType::MATRIX_MODEL )->computeCellVolumes(); diff --git a/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.cpp b/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.cpp index 14e5da41b2..0f1199c11f 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.cpp @@ -65,6 +65,7 @@ #include #include +#include #include //-------------------------------------------------------------------------------------------------- @@ -1898,10 +1899,16 @@ void RigCaseCellResultsData::testAndComputeSgasForTimeStep( size_t timeStepIndex //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RigCaseCellResultsData::computeDepthRelatedResults() +RigCaseCellResultsData::DepthResultBuffers RigCaseCellResultsData::prepareDepthRelatedResultArrays() { - size_t actCellCount = activeCellInfo()->reservoirActiveCellCount(); - if ( actCellCount == 0 ) return; + DepthResultBuffers buffers; + buffers.activeCellInfo = activeCellInfo(); + buffers.actCellCount = activeCellInfo()->reservoirActiveCellCount(); + + // A porosity model with no active cells is left disabled. + if ( buffers.actCellCount == 0 ) return buffers; + + const size_t actCellCount = buffers.actCellCount; size_t depthResultIndex = findOrLoadKnownScalarResult( RigEclipseResultAddress( RiaDefines::ResultCatType::STATIC_NATIVE, "DEPTH" ) ); size_t dxResultIndex = findOrLoadKnownScalarResult( RigEclipseResultAddress( RiaDefines::ResultCatType::STATIC_NATIVE, "DX" ) ); @@ -1910,47 +1917,40 @@ void RigCaseCellResultsData::computeDepthRelatedResults() size_t topsResultIndex = findOrLoadKnownScalarResult( RigEclipseResultAddress( RiaDefines::ResultCatType::STATIC_NATIVE, "TOPS" ) ); size_t bottomResultIndex = findOrLoadKnownScalarResult( RigEclipseResultAddress( RiaDefines::ResultCatType::STATIC_NATIVE, "BOTTOM" ) ); - bool computeDepth = false; - bool computeDx = false; - bool computeDy = false; - bool computeDz = false; - bool computeTops = false; - bool computeBottom = false; - if ( depthResultIndex == cvf::UNDEFINED_SIZE_T ) { - depthResultIndex = addStaticScalarResult( RiaDefines::ResultCatType::STATIC_NATIVE, "DEPTH", false, actCellCount ); - computeDepth = true; + depthResultIndex = addStaticScalarResult( RiaDefines::ResultCatType::STATIC_NATIVE, "DEPTH", false, actCellCount ); + buffers.computeDepth = true; } if ( dxResultIndex == cvf::UNDEFINED_SIZE_T ) { - dxResultIndex = addStaticScalarResult( RiaDefines::ResultCatType::STATIC_NATIVE, "DX", false, actCellCount ); - computeDx = true; + dxResultIndex = addStaticScalarResult( RiaDefines::ResultCatType::STATIC_NATIVE, "DX", false, actCellCount ); + buffers.computeDx = true; } if ( dyResultIndex == cvf::UNDEFINED_SIZE_T ) { - dyResultIndex = addStaticScalarResult( RiaDefines::ResultCatType::STATIC_NATIVE, "DY", false, actCellCount ); - computeDy = true; + dyResultIndex = addStaticScalarResult( RiaDefines::ResultCatType::STATIC_NATIVE, "DY", false, actCellCount ); + buffers.computeDy = true; } if ( dzResultIndex == cvf::UNDEFINED_SIZE_T ) { - dzResultIndex = addStaticScalarResult( RiaDefines::ResultCatType::STATIC_NATIVE, "DZ", false, actCellCount ); - computeDz = true; + dzResultIndex = addStaticScalarResult( RiaDefines::ResultCatType::STATIC_NATIVE, "DZ", false, actCellCount ); + buffers.computeDz = true; } if ( topsResultIndex == cvf::UNDEFINED_SIZE_T ) { - topsResultIndex = addStaticScalarResult( RiaDefines::ResultCatType::STATIC_NATIVE, "TOPS", false, actCellCount ); - computeTops = true; + topsResultIndex = addStaticScalarResult( RiaDefines::ResultCatType::STATIC_NATIVE, "TOPS", false, actCellCount ); + buffers.computeTops = true; } if ( bottomResultIndex == cvf::UNDEFINED_SIZE_T ) { - bottomResultIndex = addStaticScalarResult( RiaDefines::ResultCatType::STATIC_NATIVE, "BOTTOM", false, actCellCount ); - computeBottom = true; + bottomResultIndex = addStaticScalarResult( RiaDefines::ResultCatType::STATIC_NATIVE, "BOTTOM", false, actCellCount ); + buffers.computeBottom = true; } std::vector>& depth = m_cellScalarResults[depthResultIndex]; @@ -1965,83 +1965,144 @@ void RigCaseCellResultsData::computeDepthRelatedResults() if ( depth[0].size() < actCellCount ) { depth[0].resize( actCellCount, std::numeric_limits::max() ); - computeDepth = true; + buffers.computeDepth = true; } if ( dx[0].size() < actCellCount ) { dx[0].resize( actCellCount, std::numeric_limits::max() ); - computeDx = true; + buffers.computeDx = true; } if ( dy[0].size() < actCellCount ) { dy[0].resize( actCellCount, std::numeric_limits::max() ); - computeDy = true; + buffers.computeDy = true; } if ( dz[0].size() < actCellCount ) { dz[0].resize( actCellCount, std::numeric_limits::max() ); - computeDz = true; + buffers.computeDz = true; } if ( tops[0].size() < actCellCount ) { tops[0].resize( actCellCount, std::numeric_limits::max() ); - computeTops = true; + buffers.computeTops = true; } if ( bottom[0].size() < actCellCount ) { bottom[0].resize( actCellCount, std::numeric_limits::max() ); - computeBottom = true; + buffers.computeBottom = true; } } -#pragma omp parallel for - for ( long cellIdx = 0; cellIdx < static_cast( m_ownerMainGrid->totalCellCount() ); cellIdx++ ) - { - const RigCell& cell = m_ownerMainGrid->cell( cellIdx ); - if ( cell.isInvalid() ) continue; + buffers.depth = &depth[0]; + buffers.dx = &dx[0]; + buffers.dy = &dy[0]; + buffers.dz = &dz[0]; + buffers.tops = &tops[0]; + buffers.bottom = &bottom[0]; - size_t resultIndex = activeCellInfo()->cellResultIndex( ReservoirCellIndex( cellIdx ) ).value(); - if ( resultIndex == cvf::UNDEFINED_SIZE_T ) continue; - if ( resultIndex >= actCellCount ) continue; + return buffers; +} - bool isTemporaryGrid = cell.hostGrid()->isTempGrid(); +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RigCaseCellResultsData::computeDepthRelatedResults( RigCaseCellResultsData* matrixResults, RigCaseCellResultsData* fractureResults ) +{ + RigMainGrid* mainGrid = nullptr; + if ( matrixResults ) mainGrid = matrixResults->m_ownerMainGrid; + if ( !mainGrid && fractureResults ) mainGrid = fractureResults->m_ownerMainGrid; + if ( !mainGrid ) return; - if ( computeDepth || isTemporaryGrid ) - { - depth[0][resultIndex] = -cell.center().z(); - } + // Collect the enabled (non-empty) porosity models. Both share the same main grid, so the + // geometry of each cell only needs to be computed once and is written to every model in which + // the cell is active. + std::vector buffers; + for ( RigCaseCellResultsData* results : { matrixResults, fractureResults } ) + { + if ( !results ) continue; + DepthResultBuffers b = results->prepareDepthRelatedResultArrays(); + if ( b.actCellCount == 0 ) continue; + buffers.push_back( b ); + } - if ( computeDx || isTemporaryGrid ) - { - cvf::Vec3d cellWidth = cell.faceCenter( cvf::StructGridInterface::NEG_I ) - cell.faceCenter( cvf::StructGridInterface::POS_I ); - dx[0][resultIndex] = cellWidth.length(); - } + if ( buffers.empty() ) return; - if ( computeDy || isTemporaryGrid ) - { - cvf::Vec3d cellWidth = cell.faceCenter( cvf::StructGridInterface::NEG_J ) - cell.faceCenter( cvf::StructGridInterface::POS_J ); - dy[0][resultIndex] = cellWidth.length(); - } +#pragma omp parallel for + for ( long cellIdx = 0; cellIdx < static_cast( mainGrid->totalCellCount() ); cellIdx++ ) + { + const RigCell& cell = mainGrid->cell( cellIdx ); + if ( cell.isInvalid() ) continue; - if ( computeDz || isTemporaryGrid ) - { - cvf::Vec3d cellWidth = cell.faceCenter( cvf::StructGridInterface::NEG_K ) - cell.faceCenter( cvf::StructGridInterface::POS_K ); - dz[0][resultIndex] = cellWidth.length(); - } + const bool isTemporaryGrid = cell.hostGrid()->isTempGrid(); + + // Resolve this cell's result index in each enabled model, and gather which properties any + // active model still needs (temporary grids are always recomputed). Skip the cell entirely + // if it is inactive in every model. At most two porosity models (matrix and fracture) are + // present. + std::array resultIndices; + bool anyActive = false; + bool needDepth = isTemporaryGrid; + bool needDx = isTemporaryGrid; + bool needDy = isTemporaryGrid; + bool needDz = isTemporaryGrid; + bool needTops = isTemporaryGrid; + bool needBottom = isTemporaryGrid; + for ( size_t i = 0; i < buffers.size(); i++ ) + { + size_t resultIndex = buffers[i].activeCellInfo->cellResultIndex( ReservoirCellIndex( cellIdx ) ).value(); + if ( resultIndex == cvf::UNDEFINED_SIZE_T || resultIndex >= buffers[i].actCellCount ) + { + resultIndices[i] = cvf::UNDEFINED_SIZE_T; + continue; + } - if ( computeTops || isTemporaryGrid ) - { - tops[0][resultIndex] = -cell.faceCenter( cvf::StructGridInterface::NEG_K ).z(); - } + resultIndices[i] = resultIndex; + anyActive = true; + needDepth = needDepth || buffers[i].computeDepth; + needDx = needDx || buffers[i].computeDx; + needDy = needDy || buffers[i].computeDy; + needDz = needDz || buffers[i].computeDz; + needTops = needTops || buffers[i].computeTops; + needBottom = needBottom || buffers[i].computeBottom; + } + if ( !anyActive ) continue; + + // The geometry is shared between the porosity models, so each value is computed at most once + // (only if some active model needs it) and then written to every active model that needs it. + double depthValue = needDepth ? -cell.center().z() : 0.0; + double dxValue = + needDx ? ( cell.faceCenter( cvf::StructGridInterface::NEG_I ) - cell.faceCenter( cvf::StructGridInterface::POS_I ) ).length() : 0.0; + double dyValue = + needDy ? ( cell.faceCenter( cvf::StructGridInterface::NEG_J ) - cell.faceCenter( cvf::StructGridInterface::POS_J ) ).length() : 0.0; + double dzValue = + needDz ? ( cell.faceCenter( cvf::StructGridInterface::NEG_K ) - cell.faceCenter( cvf::StructGridInterface::POS_K ) ).length() : 0.0; + double topsValue = needTops ? -cell.faceCenter( cvf::StructGridInterface::NEG_K ).z() : 0.0; + double bottomValue = needBottom ? -cell.faceCenter( cvf::StructGridInterface::POS_K ).z() : 0.0; + + // Write a value into one model's result row, honoring its per-property compute flag. + auto assign = [&]( std::vector* row, bool compute, size_t resultIndex, double value ) + { + if ( resultIndex != cvf::UNDEFINED_SIZE_T && ( compute || isTemporaryGrid ) ) + { + ( *row )[resultIndex] = value; + } + }; - if ( computeBottom || isTemporaryGrid ) + for ( size_t i = 0; i < buffers.size(); i++ ) { - bottom[0][resultIndex] = -cell.faceCenter( cvf::StructGridInterface::POS_K ).z(); + const DepthResultBuffers& b = buffers[i]; + assign( b.depth, b.computeDepth, resultIndices[i], depthValue ); + assign( b.dx, b.computeDx, resultIndices[i], dxValue ); + assign( b.dy, b.computeDy, resultIndices[i], dyValue ); + assign( b.dz, b.computeDz, resultIndices[i], dzValue ); + assign( b.tops, b.computeTops, resultIndices[i], topsValue ); + assign( b.bottom, b.computeBottom, resultIndices[i], bottomValue ); } } } diff --git a/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.h b/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.h index 0055789d65..bf7a4c1d1a 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.h +++ b/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.h @@ -142,7 +142,12 @@ class RigCaseCellResultsData : public cvf::Object void createResultEntry( const RigEclipseResultAddress& resultAddress, bool needsToBeStored ); bool updateResultDataType( const RigEclipseResultAddress& resultAddress, RiaDefines::ResultDataType dataType ); void createPlaceholderResultEntries(); - void computeDepthRelatedResults(); + + // Compute depth-related geometry results (DEPTH/DX/DY/DZ/TOPS/BOTTOM) for both porosity models + // in a single traversal of the shared grid. The values are derived purely from the grid + // geometry, so a cell active in both models is computed only once and written to both. + static void computeDepthRelatedResults( RigCaseCellResultsData* matrixResults, RigCaseCellResultsData* fractureResults ); + void computeCellVolumes(); bool hasFlowDiagUsableFluxes() const; @@ -160,6 +165,28 @@ class RigCaseCellResultsData : public cvf::Object void clearAllResultAliases(); private: + // Per-model buffers used by computeDepthRelatedResults(). Holds pointers to the result rows + // and per-property flags telling whether the row still needs to be computed. A buffer with + // actCellCount == 0 is disabled (the porosity model has no active cells). + struct DepthResultBuffers + { + RigActiveCellInfo* activeCellInfo = nullptr; + size_t actCellCount = 0; + std::vector* depth = nullptr; + std::vector* dx = nullptr; + std::vector* dy = nullptr; + std::vector* dz = nullptr; + std::vector* tops = nullptr; + std::vector* bottom = nullptr; + bool computeDepth = false; + bool computeDx = false; + bool computeDy = false; + bool computeDz = false; + bool computeTops = false; + bool computeBottom = false; + }; + DepthResultBuffers prepareDepthRelatedResultArrays(); + size_t findOrLoadKnownScalarResult( const RigEclipseResultAddress& resVarAddr ); size_t findOrLoadKnownScalarResultByResultTypeOrder( const RigEclipseResultAddress& resVarAddr, const std::vector& resultCategorySearchOrder ); diff --git a/ApplicationLibCode/ReservoirDataModel/RigEclipseCaseData.cpp b/ApplicationLibCode/ReservoirDataModel/RigEclipseCaseData.cpp index ebd44f42bd..c2446f6249 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigEclipseCaseData.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigEclipseCaseData.cpp @@ -467,6 +467,14 @@ void RigEclipseCaseData::computeActiveCellBoundingBoxes( bool useOptimizedVersio computeActiveCellsGeometryBoundingBoxSlow(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RigEclipseCaseData::computeDepthRelatedResults() +{ + RigCaseCellResultsData::computeDepthRelatedResults( m_matrixModelResults.p(), m_fractureModelResults.p() ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ReservoirDataModel/RigEclipseCaseData.h b/ApplicationLibCode/ReservoirDataModel/RigEclipseCaseData.h index 6564232e79..07886a9747 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigEclipseCaseData.h +++ b/ApplicationLibCode/ReservoirDataModel/RigEclipseCaseData.h @@ -104,6 +104,8 @@ class RigEclipseCaseData : public cvf::Object void computeActiveCellBoundingBoxes( bool useOptimizedVersion ); + void computeDepthRelatedResults(); + RiaDefines::EclipseUnitSystem unitsType() const { return m_unitsType; } void setUnitsType( RiaDefines::EclipseUnitSystem unitsType ) { m_unitsType = unitsType; } diff --git a/ApplicationLibCode/ReservoirDataModel/RigReservoirGridTools.cpp b/ApplicationLibCode/ReservoirDataModel/RigReservoirGridTools.cpp index 01d1a9ae48..80e989e4a4 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigReservoirGridTools.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigReservoirGridTools.cpp @@ -197,16 +197,12 @@ void RigReservoirGridTools::computeCachedData( RimEclipseCase* eclipseCase ) eclipseCase->computeActiveCellsBoundingBox(); } + RigCaseCellResultsData::computeDepthRelatedResults( cellResultsDataMatrix, cellResultsDataFracture ); + if ( cellResultsDataMatrix ) { - cellResultsDataMatrix->computeDepthRelatedResults(); cellResultsDataMatrix->computeCellVolumes(); } - - if ( cellResultsDataFracture ) - { - cellResultsDataFracture->computeDepthRelatedResults(); - } } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/UnitTests/RifReaderEclipseOutput-Test.cpp b/ApplicationLibCode/UnitTests/RifReaderEclipseOutput-Test.cpp index f29627921a..f431982d4f 100644 --- a/ApplicationLibCode/UnitTests/RifReaderEclipseOutput-Test.cpp +++ b/ApplicationLibCode/UnitTests/RifReaderEclipseOutput-Test.cpp @@ -30,13 +30,17 @@ #include "RifEclipseOutputFileTools.h" #include "RifEclipseUnifiedRestartFileAccess.h" #include "RifReaderEclipseOutput.h" +#include "RigActiveCellInfo.h" #include "RigCaseCellResultsData.h" #include "RigEclipseCaseData.h" +#include "RigEclipseResultAddress.h" +#include "RigMainGrid.h" #include "RimEclipseResultCase.h" #include #include +#include #include using namespace RiaDefines; @@ -95,6 +99,85 @@ TEST( RigReservoirTest, BasicTest10k ) } } +//-------------------------------------------------------------------------------------------------- +/// Regression guard for issue #14227. The depth-related geometry results +/// (DEPTH/DX/DY/DZ/TOPS/BOTTOM) are computed independently for the matrix and fracture porosity +/// models. They are derived purely from the shared grid geometry, so for a dual-porosity model a +/// cell that is active in both models must receive identical values in each model. +//-------------------------------------------------------------------------------------------------- +TEST( RigReservoirTest, DualPorosityDepthResults ) +{ + QDir baseFolder( TEST_MODEL_DIR ); + bool subFolderExists = baseFolder.cd( "dualporo-testcase" ); + EXPECT_TRUE( subFolderExists ); + QString filename( "DUALPORO-WELL-COMPLETION-DEFAULT.EGRID" ); + QString filePath = baseFolder.absoluteFilePath( filename ); + EXPECT_TRUE( QFile::exists( filePath ) ); + + std::unique_ptr resultCase( new RimEclipseResultCase ); + cvf::ref reservoir = new RigEclipseCaseData( resultCase.get() ); + + cvf::ref readerInterfaceEcl = new RifReaderEclipseOutput; + bool result = readerInterfaceEcl->open( filePath, reservoir.p() ); + EXPECT_TRUE( result ); + + RigCaseCellResultsData* matrixResults = reservoir->results( PorosityModelType::MATRIX_MODEL ); + RigCaseCellResultsData* fractureResults = reservoir->results( PorosityModelType::FRACTURE_MODEL ); + ASSERT_TRUE( matrixResults != nullptr ); + ASSERT_TRUE( fractureResults != nullptr ); + + RigActiveCellInfo* matrixActiveInfo = reservoir->activeCellInfo( PorosityModelType::MATRIX_MODEL ); + RigActiveCellInfo* fractureActiveInfo = reservoir->activeCellInfo( PorosityModelType::FRACTURE_MODEL ); + ASSERT_TRUE( matrixActiveInfo != nullptr ); + ASSERT_TRUE( fractureActiveInfo != nullptr ); + + // This model must actually exercise both porosity models, otherwise the test is meaningless. + EXPECT_GT( matrixActiveInfo->reservoirActiveCellCount(), size_t( 0 ) ); + EXPECT_GT( fractureActiveInfo->reservoirActiveCellCount(), size_t( 0 ) ); + + // Compute depth-related geometry results for both porosity models. + reservoir->computeDepthRelatedResults(); + + const QStringList propertyNames = { "DEPTH", "DX", "DY", "DZ", "TOPS", "BOTTOM" }; + + const double undefinedValue = std::numeric_limits::max(); + const size_t totalCellCount = reservoir->mainGrid()->totalCellCount(); + + for ( const QString& propertyName : propertyNames ) + { + RigEclipseResultAddress resAddr( ResultCatType::STATIC_NATIVE, propertyName ); + + EXPECT_TRUE( matrixResults->hasResultEntry( resAddr ) ); + EXPECT_TRUE( fractureResults->hasResultEntry( resAddr ) ); + + const std::vector& matrixValues = matrixResults->cellScalarResults( resAddr, 0 ); + const std::vector& fractureValues = fractureResults->cellScalarResults( resAddr, 0 ); + + EXPECT_EQ( matrixValues.size(), matrixActiveInfo->reservoirActiveCellCount() ); + EXPECT_EQ( fractureValues.size(), fractureActiveInfo->reservoirActiveCellCount() ); + + // For every reservoir cell active in both models, the geometry-derived value must match. + size_t comparedCellCount = 0; + for ( size_t cellIdx = 0; cellIdx < totalCellCount; cellIdx++ ) + { + size_t matrixResultIdx = matrixActiveInfo->cellResultIndex( ReservoirCellIndex( cellIdx ) ).value(); + size_t fractureResultIdx = fractureActiveInfo->cellResultIndex( ReservoirCellIndex( cellIdx ) ).value(); + if ( matrixResultIdx == cvf::UNDEFINED_SIZE_T || fractureResultIdx == cvf::UNDEFINED_SIZE_T ) continue; + if ( matrixResultIdx >= matrixValues.size() || fractureResultIdx >= fractureValues.size() ) continue; + + const double matrixValue = matrixValues[matrixResultIdx]; + const double fractureValue = fractureValues[fractureResultIdx]; + + EXPECT_NE( matrixValue, undefinedValue ); + EXPECT_NE( fractureValue, undefinedValue ); + EXPECT_DOUBLE_EQ( matrixValue, fractureValue ); + comparedCellCount++; + } + + EXPECT_GT( comparedCellCount, size_t( 0 ) ); + } +} + TEST( RigReservoirTest, BasicTest10kRestart ) { RifEclipseUnifiedRestartFileAccess unrstAccess;