diff --git a/ApplicationLibCode/Application/RiaDefines.cpp b/ApplicationLibCode/Application/RiaDefines.cpp index bc8ad72f1a..9dd8939ade 100644 --- a/ApplicationLibCode/Application/RiaDefines.cpp +++ b/ApplicationLibCode/Application/RiaDefines.cpp @@ -62,6 +62,15 @@ void caf::AppEnum::setUp() setDefault( RiaDefines::DepthUnitType::UNIT_METER ); } +template <> +void caf::AppEnum::setUp() +{ + addItem( RiaDefines::GridModelReader::LIBECL, "LIBECL", "libecl" ); + addItem( RiaDefines::GridModelReader::OPM_COMMON, "OPM_COMMON", "opm-common (beta)" ); + + setDefault( RiaDefines::GridModelReader::LIBECL ); +} + template <> void caf::AppEnum::setUp() { diff --git a/ApplicationLibCode/Application/RiaDefines.h b/ApplicationLibCode/Application/RiaDefines.h index 097196c77f..27481879ea 100644 --- a/ApplicationLibCode/Application/RiaDefines.h +++ b/ApplicationLibCode/Application/RiaDefines.h @@ -167,6 +167,12 @@ enum class GridCaseAxis UNDEFINED_AXIS }; +enum class GridModelReader +{ + LIBECL, + OPM_COMMON +}; + enum class ThemeEnum { DEFAULT, diff --git a/ApplicationLibCode/Application/RiaEclipseFileNameTools.cpp b/ApplicationLibCode/Application/RiaEclipseFileNameTools.cpp index e8b39586b2..fb1bc9f62a 100644 --- a/ApplicationLibCode/Application/RiaEclipseFileNameTools.cpp +++ b/ApplicationLibCode/Application/RiaEclipseFileNameTools.cpp @@ -28,6 +28,7 @@ void caf::AppEnum::setUp() addItem( RiaEclipseFileNameTools::EclipseFileType::ECLIPSE_DATA, "DATA", "Data Deck" ); addItem( RiaEclipseFileNameTools::EclipseFileType::ECLIPSE_GRID, "GRID", "Grid" ); addItem( RiaEclipseFileNameTools::EclipseFileType::ECLIPSE_EGRID, "EGRID", "Grid" ); + addItem( RiaEclipseFileNameTools::EclipseFileType::ECLIPSE_INIT, "INIT", "Init file" ); addItem( RiaEclipseFileNameTools::EclipseFileType::ECLIPSE_UNRST, "UNRST", "Unified Restart" ); addItem( RiaEclipseFileNameTools::EclipseFileType::ECLIPSE_SMSPEC, "SMSPEC", "Summary Specification" ); addItem( RiaEclipseFileNameTools::EclipseFileType::ECLIPSE_UNSMRY, "UNSMR", "Summary Vectors" ); diff --git a/ApplicationLibCode/Application/RiaEclipseFileNameTools.h b/ApplicationLibCode/Application/RiaEclipseFileNameTools.h index 8a09ab9fc3..a7064ff5d6 100644 --- a/ApplicationLibCode/Application/RiaEclipseFileNameTools.h +++ b/ApplicationLibCode/Application/RiaEclipseFileNameTools.h @@ -35,6 +35,7 @@ public: ECLIPSE_DATA, ECLIPSE_GRID, ECLIPSE_EGRID, + ECLIPSE_INIT, ECLIPSE_UNRST, ECLIPSE_SMSPEC, ECLIPSE_UNSMRY, diff --git a/ApplicationLibCode/Application/RiaPreferences.cpp b/ApplicationLibCode/Application/RiaPreferences.cpp index fe61f1819a..3351ba7efd 100644 --- a/ApplicationLibCode/Application/RiaPreferences.cpp +++ b/ApplicationLibCode/Application/RiaPreferences.cpp @@ -192,6 +192,8 @@ RiaPreferences::RiaPreferences() CAF_PDM_InitField( &csvTextExportFieldSeparator, "csvTextExportFieldSeparator", QString( "," ), "CSV Text Export Field Separator" ); + CAF_PDM_InitFieldNoDefault( &m_gridModelReader, "gridModelReader", "Grid Model Reader" ); + CAF_PDM_InitFieldNoDefault( &m_readerSettings, "readerSettings", "Reader Settings" ); m_readerSettings = new RifReaderSettings; CAF_PDM_InitFieldNoDefault( &m_dateFormat, "dateFormat", "Date Format" ); @@ -357,6 +359,8 @@ void RiaPreferences::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& } else if ( uiConfigName == RiaPreferences::tabNameGrid() ) { + uiOrdering.add( &m_gridModelReader ); + caf::PdmUiGroup* newCaseBehaviourGroup = uiOrdering.addNewGroup( "Behavior When Loading Data" ); newCaseBehaviourGroup->add( &autocomputeDepthRelatedProperties ); newCaseBehaviourGroup->add( &loadAndShowSoil ); @@ -629,6 +633,14 @@ const RifReaderSettings* RiaPreferences::readerSettings() const return m_readerSettings; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RiaDefines::GridModelReader RiaPreferences::gridModelReader() const +{ + return m_gridModelReader(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/Application/RiaPreferences.h b/ApplicationLibCode/Application/RiaPreferences.h index 9af51b9741..1157fa1433 100644 --- a/ApplicationLibCode/Application/RiaPreferences.h +++ b/ApplicationLibCode/Application/RiaPreferences.h @@ -55,6 +55,7 @@ public: using FontSizeEnum = RiaFontCache::FontSizeEnum; using PageSizeEnum = caf::AppEnum; using PageOrientationEnum = caf::AppEnum; + using GridModelEnum = caf::AppEnum; bool enableFaultsByDefault() const; @@ -66,7 +67,8 @@ public: QStringList tabNames(); - const RifReaderSettings* readerSettings() const; + const RifReaderSettings* readerSettings() const; + RiaDefines::GridModelReader gridModelReader() const; bool useUndoRedo() const; @@ -165,6 +167,7 @@ private: static double defaultMarginSize( QPageSize::PageSizeId pageSizeId ); private: + caf::PdmField m_gridModelReader; caf::PdmChildField m_readerSettings; caf::PdmField m_dateFormat; diff --git a/ApplicationLibCode/CMakeLists.txt b/ApplicationLibCode/CMakeLists.txt index 7fd5f80b9d..aeb44e7c1c 100644 --- a/ApplicationLibCode/CMakeLists.txt +++ b/ApplicationLibCode/CMakeLists.txt @@ -202,7 +202,7 @@ list(APPEND RI_PRIVATE_INCLUDES ${RESINSIGHT_OPENVDS_API_DIR}/include) # Configure include directories if opm-common.lib is downloaded and linked # without building from source # -if(NOT BUILD_FROM_SOURCE) +if(NOT RESINSIGHT_BUILD_LIBS_FROM_SOURCE) list(APPEND RI_PUBLIC_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/../ThirdParty/custom-opm-common/opm-common ) diff --git a/ApplicationLibCode/FileInterface/CMakeLists_files.cmake b/ApplicationLibCode/FileInterface/CMakeLists_files.cmake index a1da0f6c25..af85f5b88e 100644 --- a/ApplicationLibCode/FileInterface/CMakeLists_files.cmake +++ b/ApplicationLibCode/FileInterface/CMakeLists_files.cmake @@ -85,6 +85,7 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RifRevealSummaryCsvReader.h ${CMAKE_CURRENT_LIST_DIR}/RifRevealCsvSectionSummaryReader.h ${CMAKE_CURRENT_LIST_DIR}/RifStimPlanCsvSummaryReader.h + ${CMAKE_CURRENT_LIST_DIR}/RifReaderOpmCommon.h ) set(SOURCE_GROUP_SOURCE_FILES @@ -171,6 +172,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RifRevealCsvSummaryReader.cpp ${CMAKE_CURRENT_LIST_DIR}/RifRevealCsvSectionSummaryReader.cpp ${CMAKE_CURRENT_LIST_DIR}/RifStimPlanCsvSummaryReader.cpp + ${CMAKE_CURRENT_LIST_DIR}/RifReaderOpmCommon.cpp ) list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES}) diff --git a/ApplicationLibCode/FileInterface/RifEclipseOutputFileTools.cpp b/ApplicationLibCode/FileInterface/RifEclipseOutputFileTools.cpp index 05f9a87772..83dc8c0115 100644 --- a/ApplicationLibCode/FileInterface/RifEclipseOutputFileTools.cpp +++ b/ApplicationLibCode/FileInterface/RifEclipseOutputFileTools.cpp @@ -22,9 +22,17 @@ #include "RiaQDateTimeTools.h" #include "RiaStringEncodingTools.h" + #include "RifEclipseRestartFilesetAccess.h" #include "RifEclipseUnifiedRestartFileAccess.h" +#include "RigActiveCellInfo.h" +#include "RigCaseCellResultsData.h" +#include "RigEclipseCaseData.h" +#include "RigEclipseResultAddress.h" +#include "RigEclipseResultInfo.h" +#include "RigMainGrid.h" + #include "ert/ecl/ecl_file.h" #include "ert/ecl/ecl_grid.h" #include "ert/ecl/ecl_kw_magic.h" @@ -65,6 +73,54 @@ std::vector RifEclipseOutputFileTools::keywordValueCounts( return reportstepMetaData.keywordValueCounts(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RifEclipseOutputFileTools::createResultEntries( const std::vector& fileKeywordInfo, + const std::vector& timeStepInfo, + RiaDefines::ResultCatType resultCategory, + RigEclipseCaseData* eclipseCaseData ) +{ + if ( !eclipseCaseData ) return; + + RigCaseCellResultsData* matrixModelResults = eclipseCaseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL ); + RigCaseCellResultsData* fractureModelResults = eclipseCaseData->results( RiaDefines::PorosityModelType::FRACTURE_MODEL ); + + { + auto validKeywords = validKeywordsForPorosityModel( fileKeywordInfo, + eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ), + eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL ), + RiaDefines::PorosityModelType::MATRIX_MODEL, + timeStepInfo.size() ); + + for ( const auto& keywordData : validKeywords ) + { + RigEclipseResultAddress resAddr( resultCategory, + RifKeywordValueCount::mapType( keywordData.dataType() ), + QString::fromStdString( keywordData.keyword() ) ); + matrixModelResults->createResultEntry( resAddr, false ); + matrixModelResults->setTimeStepInfos( resAddr, timeStepInfo ); + } + } + + { + auto validKeywords = validKeywordsForPorosityModel( fileKeywordInfo, + eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ), + eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL ), + RiaDefines::PorosityModelType::FRACTURE_MODEL, + timeStepInfo.size() ); + + for ( const auto& keywordData : validKeywords ) + { + RigEclipseResultAddress resAddr( resultCategory, + RifKeywordValueCount::mapType( keywordData.dataType() ), + QString::fromStdString( keywordData.keyword() ) ); + fractureModelResults->createResultEntry( resAddr, false ); + fractureModelResults->setTimeStepInfos( resAddr, timeStepInfo ); + } + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -593,6 +649,73 @@ FILE* RifEclipseOutputFileTools::fopen( const QString& filePath, const QString& return filePtr; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifEclipseOutputFileTools::assignActiveCellData( std::vector>& actnumValuesPerGrid, RigEclipseCaseData* eclipseCaseData ) +{ + size_t reservoirCellCount = 0; + for ( const auto& actnumValues : actnumValuesPerGrid ) + { + reservoirCellCount += actnumValues.size(); + } + + // Check if number of cells is matching + if ( eclipseCaseData->mainGrid()->globalCellArray().size() != reservoirCellCount ) + { + return false; + } + + RigActiveCellInfo* activeCellInfo = eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ); + RigActiveCellInfo* fractureActiveCellInfo = eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL ); + + activeCellInfo->setReservoirCellCount( reservoirCellCount ); + fractureActiveCellInfo->setReservoirCellCount( reservoirCellCount ); + activeCellInfo->setGridCount( actnumValuesPerGrid.size() ); + fractureActiveCellInfo->setGridCount( actnumValuesPerGrid.size() ); + + size_t cellIdx = 0; + size_t globalActiveMatrixIndex = 0; + size_t globalActiveFractureIndex = 0; + + const int matrixActive = 1; + const int fractureActive = 2; + const int matrixAndFractureActive = 3; + + for ( size_t gridIndex = 0; gridIndex < actnumValuesPerGrid.size(); gridIndex++ ) + { + size_t activeMatrixIndex = 0; + size_t activeFractureIndex = 0; + + std::vector& actnumValues = actnumValuesPerGrid[gridIndex]; + + for ( int actnumValue : actnumValues ) + { + if ( actnumValue == matrixActive || actnumValue == matrixAndFractureActive ) + { + activeCellInfo->setCellResultIndex( cellIdx, globalActiveMatrixIndex++ ); + activeMatrixIndex++; + } + + if ( actnumValue == fractureActive || actnumValue == matrixAndFractureActive ) + { + fractureActiveCellInfo->setCellResultIndex( cellIdx, globalActiveFractureIndex++ ); + activeFractureIndex++; + } + + cellIdx++; + } + + activeCellInfo->setGridActiveCellCounts( gridIndex, activeMatrixIndex ); + fractureActiveCellInfo->setGridActiveCellCounts( gridIndex, activeFractureIndex ); + } + + activeCellInfo->computeDerivedData(); + fractureActiveCellInfo->computeDerivedData(); + + return true; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -664,3 +787,100 @@ RifRestartReportKeywords RifEclipseOutputFileTools::createReportStepsMetaData( c return reportSteps; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector + RifEclipseOutputFileTools::validKeywordsForPorosityModel( const std::vector& keywordItemCounts, + const RigActiveCellInfo* matrixActiveCellInfo, + const RigActiveCellInfo* fractureActiveCellInfo, + RiaDefines::PorosityModelType porosityModel, + size_t timeStepCount ) +{ + if ( !matrixActiveCellInfo ) return {}; + + if ( porosityModel == RiaDefines::PorosityModelType::FRACTURE_MODEL && ( fractureActiveCellInfo->reservoirActiveCellCount() == 0 ) ) + { + return {}; + } + + std::vector keywordsWithCorrectNumberOfDataItems; + + for ( const auto& keywordValueCount : keywordItemCounts ) + { + QString keyword = QString::fromStdString( keywordValueCount.keyword() ); + size_t valueCount = keywordValueCount.valueCount(); + + bool validKeyword = false; + + size_t timeStepsAllCellsRest = valueCount % matrixActiveCellInfo->reservoirCellCount(); + if ( timeStepsAllCellsRest == 0 && valueCount <= timeStepCount * matrixActiveCellInfo->reservoirCellCount() ) + { + // Found result for all cells for N time steps, usually a static dataset for one time step + validKeyword = true; + } + else + { + size_t timeStepsMatrixRest = valueCount % matrixActiveCellInfo->reservoirActiveCellCount(); + + size_t timeStepsFractureRest = 0; + if ( fractureActiveCellInfo->reservoirActiveCellCount() > 0 ) + { + timeStepsFractureRest = valueCount % fractureActiveCellInfo->reservoirActiveCellCount(); + } + + size_t sumFractureMatrixActiveCellCount = matrixActiveCellInfo->reservoirActiveCellCount() + + fractureActiveCellInfo->reservoirActiveCellCount(); + size_t timeStepsMatrixAndFractureRest = valueCount % sumFractureMatrixActiveCellCount; + + if ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL && timeStepsMatrixRest == 0 ) + { + if ( valueCount <= + timeStepCount * std::max( matrixActiveCellInfo->reservoirActiveCellCount(), sumFractureMatrixActiveCellCount ) ) + { + validKeyword = true; + } + } + else if ( porosityModel == RiaDefines::PorosityModelType::FRACTURE_MODEL && + fractureActiveCellInfo->reservoirActiveCellCount() > 0 && timeStepsFractureRest == 0 ) + { + if ( valueCount <= + timeStepCount * std::max( fractureActiveCellInfo->reservoirActiveCellCount(), sumFractureMatrixActiveCellCount ) ) + { + validKeyword = true; + } + } + else if ( timeStepsMatrixAndFractureRest == 0 ) + { + if ( valueCount <= timeStepCount * sumFractureMatrixActiveCellCount ) + { + validKeyword = true; + } + } + } + + // Check for INIT values that has only values for main grid active cells + if ( !validKeyword ) + { + if ( timeStepCount == 1 ) + { + size_t mainGridMatrixActiveCellCount = matrixActiveCellInfo->gridActiveCellCounts( 0 ); + size_t mainGridFractureActiveCellCount = fractureActiveCellInfo->gridActiveCellCounts( 0 ); + + if ( valueCount == mainGridMatrixActiveCellCount || valueCount == mainGridFractureActiveCellCount || + valueCount == mainGridMatrixActiveCellCount + mainGridFractureActiveCellCount ) + { + validKeyword = true; + } + } + } + + if ( validKeyword ) + { + keywordsWithCorrectNumberOfDataItems.push_back( keywordValueCount ); + } + } + + return keywordsWithCorrectNumberOfDataItems; +} diff --git a/ApplicationLibCode/FileInterface/RifEclipseOutputFileTools.h b/ApplicationLibCode/FileInterface/RifEclipseOutputFileTools.h index 10d63b425b..7301ed2420 100644 --- a/ApplicationLibCode/FileInterface/RifEclipseOutputFileTools.h +++ b/ApplicationLibCode/FileInterface/RifEclipseOutputFileTools.h @@ -35,6 +35,8 @@ using ecl_file_type = struct ecl_file_struct; class RifEclipseRestartDataAccess; +class RigEclipseTimeStepInfo; +class RigActiveCellInfo; class QByteArray; //================================================================================================== @@ -50,6 +52,11 @@ public: static std::vector keywordValueCounts( const std::vector& ecl_files ); + static void createResultEntries( const std::vector& fileKeywordInfo, + const std::vector& timeStepInfo, + RiaDefines::ResultCatType resultCategory, + RigEclipseCaseData* eclipseCaseData ); + static bool keywordData( const ecl_file_type* ecl_file, const QString& keyword, size_t fileKeywordOccurrence, std::vector* values ); static bool keywordData( const ecl_file_type* ecl_file, const QString& keyword, size_t fileKeywordOccurrence, std::vector* values ); @@ -85,6 +92,13 @@ public: static FILE* fopen( const QString& filePath, const QString& mode ); + static bool assignActiveCellData( std::vector>& actnumValuesPerGrid, RigEclipseCaseData* eclipseCaseData ); + private: - static RifRestartReportKeywords createReportStepsMetaData( const std::vector& ecl_files ); + static RifRestartReportKeywords createReportStepsMetaData( const std::vector& ecl_files ); + static std::vector validKeywordsForPorosityModel( const std::vector& keywordItemCounts, + const RigActiveCellInfo* activeCellInfo, + const RigActiveCellInfo* fractureActiveCellInfo, + RiaDefines::PorosityModelType matrixOrFracture, + size_t timeStepCount ); }; diff --git a/ApplicationLibCode/FileInterface/RifOpmGridTools.cpp b/ApplicationLibCode/FileInterface/RifOpmGridTools.cpp index 738abf5711..2789556f95 100644 --- a/ApplicationLibCode/FileInterface/RifOpmGridTools.cpp +++ b/ApplicationLibCode/FileInterface/RifOpmGridTools.cpp @@ -23,6 +23,8 @@ #include "RifReaderEclipseOutput.h" +#include "RigActiveCellInfo.h" +#include "RigEclipseCaseData.h" #include "RigMainGrid.h" #include "cvfGeometryTools.h" @@ -98,6 +100,61 @@ void RifOpmGridTools::importCoordinatesForRadialGrid( const std::string& gridFil } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +size_t RifOpmGridTools::cellCount( const std::string& gridFilePath ) +{ + Opm::EclIO::EGrid opmGrid( gridFilePath ); + + return opmGrid.totalNumberOfCells(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifOpmGridTools::importGrid( const std::string& gridFilePath, RigMainGrid* mainGrid, RigEclipseCaseData* caseData ) +{ + Opm::EclIO::EGrid opmGrid( gridFilePath ); + + auto dims = opmGrid.dimension(); + mainGrid->setGridPointDimensions( cvf::Vec3st( dims[0] + 1, dims[1] + 1, dims[2] + 1 ) ); + + RigCell defaultCell; + defaultCell.setHostGrid( mainGrid ); + auto cellCount = opmGrid.totalNumberOfCells(); + mainGrid->globalCellArray().resize( cellCount, defaultCell ); + mainGrid->nodes().resize( 8 * cellCount ); + + transferCoordinatesCartesian( opmGrid, opmGrid, mainGrid, mainGrid, caseData ); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector> RifOpmGridTools::activeCellsFromActnumKeyword( Opm::EclIO::EGrid& grid ) +{ + auto arrayNames = grid.arrayNames(); + int actnumArrayIndex = -1; + + for ( size_t i = 0; i < arrayNames.size(); i++ ) + { + if ( arrayNames[i] == "ACTNUM" ) + { + actnumArrayIndex = static_cast( i ); + break; + } + } + + if ( actnumArrayIndex < 0 ) return {}; + + auto actnumMainGrid = grid.get( actnumArrayIndex ); + + return { actnumMainGrid }; +} + //-------------------------------------------------------------------------------------------------- // // A radial grid is defined by a center point and a set of cylindrical coordinates. The coordinates at the @@ -251,6 +308,79 @@ void RifOpmGridTools::transferCoordinates( Opm::EclIO::EGrid& opmMainGrid, Opm:: } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RifOpmGridTools::transferCoordinatesCartesian( Opm::EclIO::EGrid& opmMainGrid, + Opm::EclIO::EGrid& opmGrid, + RigMainGrid* riMainGrid, + RigGridBase* riGrid, + RigEclipseCaseData* caseData ) +{ + // Prefix OPM structures with _opm_and ResInsight structures with _ri_ + + auto& riNodes = riMainGrid->nodes(); + + opmGrid.loadData(); + opmGrid.load_grid_data(); + + auto riActiveCells = caseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ); + auto riActiveCellsFrac = caseData->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL ); + riActiveCellsFrac->setGridCount( 1 ); + riActiveCellsFrac->setGridActiveCellCounts( 0, 0 ); + + riActiveCells->setReservoirCellCount( riMainGrid->cellCount() ); + + // same mapping as libecl + const size_t cellMappingECLRi[8] = { 0, 1, 3, 2, 4, 5, 7, 6 }; + +#pragma omp parallel for + for ( int opmCellIndex = 0; opmCellIndex < static_cast( riMainGrid->cellCount() ); opmCellIndex++ ) + { + auto opmIJK = opmGrid.ijk_from_global_index( opmCellIndex ); + + auto riReservoirIndex = riGrid->cellIndexFromIJK( opmIJK[0], opmIJK[1], opmIJK[2] ); + RigCell& cell = riMainGrid->globalCellArray()[riReservoirIndex]; + cell.setGridLocalCellIndex( riReservoirIndex ); + + std::array opmX{}; + std::array opmY{}; + std::array opmZ{}; + opmGrid.getCellCorners( opmCellIndex, opmX, opmY, opmZ ); + + // Each cell has 8 nodes, use reservoir cell index and multiply to find first node index for cell + auto riNodeStartIndex = riReservoirIndex * 8; + + for ( size_t opmNodeIndex = 0; opmNodeIndex < 8; opmNodeIndex++ ) + { + auto riCornerIndex = cellMappingECLRi[opmNodeIndex]; + size_t riNodeIndex = riNodeStartIndex + riCornerIndex; + + auto& riNode = riNodes[riNodeIndex]; + riNode.x() = opmX[opmNodeIndex]; + riNode.y() = opmY[opmNodeIndex]; + riNode.z() = -opmZ[opmNodeIndex]; + + cell.cornerIndices()[riCornerIndex] = riNodeIndex; + } + + if ( riActiveCells ) + { + auto activeIndex = opmGrid.active_index( opmIJK[0], opmIJK[1], opmIJK[2] ); + if ( activeIndex > -1 ) + { + riActiveCells->setCellResultIndex( riReservoirIndex, activeIndex ); + } + } + } + + riActiveCells->setGridCount( 1 ); + riActiveCells->setGridActiveCellCounts( 0, opmGrid.activeCells() ); + riActiveCells->computeDerivedData(); + + riMainGrid->initAllSubGridsParentGridPointer(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/FileInterface/RifOpmGridTools.h b/ApplicationLibCode/FileInterface/RifOpmGridTools.h index e7cdca32d6..50df2896b3 100644 --- a/ApplicationLibCode/FileInterface/RifOpmGridTools.h +++ b/ApplicationLibCode/FileInterface/RifOpmGridTools.h @@ -34,6 +34,7 @@ namespace EclIO class RigMainGrid; class RigGridBase; +class RigEclipseCaseData; //================================================================================================== /// @@ -44,8 +45,18 @@ public: // If the grid is radial, the coordinates are imported and adjusted to fit the host cells static void importCoordinatesForRadialGrid( const std::string& gridFilePath, RigMainGrid* mainGrid ); + static size_t cellCount( const std::string& gridFilePath ); + static bool importGrid( const std::string& gridFilePath, RigMainGrid* mainGrid, RigEclipseCaseData* caseData ); + + static std::vector> activeCellsFromActnumKeyword( Opm::EclIO::EGrid& grid ); + private: static void transferCoordinates( Opm::EclIO::EGrid& opmMainGrid, Opm::EclIO::EGrid& opmGrid, RigMainGrid* riMainGrid, RigGridBase* riGrid ); + static void transferCoordinatesCartesian( Opm::EclIO::EGrid& opmMainGrid, + Opm::EclIO::EGrid& opmGrid, + RigMainGrid* riMainGrid, + RigGridBase* riGrid, + RigEclipseCaseData* caseData ); static std::map> computeXyCenterForTopOfCells( Opm::EclIO::EGrid& opmMainGrid, Opm::EclIO::EGrid& opmGrid, RigGridBase* riGrid ); diff --git a/ApplicationLibCode/FileInterface/RifReaderEclipseOutput.cpp b/ApplicationLibCode/FileInterface/RifReaderEclipseOutput.cpp index 543d9c35e9..1d8ea15f8f 100644 --- a/ApplicationLibCode/FileInterface/RifReaderEclipseOutput.cpp +++ b/ApplicationLibCode/FileInterface/RifReaderEclipseOutput.cpp @@ -837,62 +837,7 @@ bool RifReaderEclipseOutput::readActiveCellInfo() } } - size_t reservoirCellCount = 0; - for ( const auto& actnumValues : actnumValuesPerGrid ) - { - reservoirCellCount += actnumValues.size(); - } - - // Check if number of cells is matching - if ( m_eclipseCase->mainGrid()->globalCellArray().size() != reservoirCellCount ) - { - return false; - } - - RigActiveCellInfo* activeCellInfo = m_eclipseCase->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ); - RigActiveCellInfo* fractureActiveCellInfo = m_eclipseCase->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL ); - - activeCellInfo->setReservoirCellCount( reservoirCellCount ); - fractureActiveCellInfo->setReservoirCellCount( reservoirCellCount ); - activeCellInfo->setGridCount( actnumValuesPerGrid.size() ); - fractureActiveCellInfo->setGridCount( actnumValuesPerGrid.size() ); - - size_t cellIdx = 0; - size_t globalActiveMatrixIndex = 0; - size_t globalActiveFractureIndex = 0; - - for ( size_t gridIndex = 0; gridIndex < actnumValuesPerGrid.size(); gridIndex++ ) - { - size_t activeMatrixIndex = 0; - size_t activeFractureIndex = 0; - - std::vector& actnumValues = actnumValuesPerGrid[gridIndex]; - - for ( int actnumValue : actnumValues ) - { - if ( actnumValue == 1 || actnumValue == 3 ) - { - activeCellInfo->setCellResultIndex( cellIdx, globalActiveMatrixIndex++ ); - activeMatrixIndex++; - } - - if ( actnumValue == 2 || actnumValue == 3 ) - { - fractureActiveCellInfo->setCellResultIndex( cellIdx, globalActiveFractureIndex++ ); - activeFractureIndex++; - } - - cellIdx++; - } - - activeCellInfo->setGridActiveCellCounts( gridIndex, activeMatrixIndex ); - fractureActiveCellInfo->setGridActiveCellCounts( gridIndex, activeFractureIndex ); - } - - activeCellInfo->computeDerivedData(); - fractureActiveCellInfo->computeDerivedData(); - - return true; + return RifEclipseOutputFileTools::assignActiveCellData( actnumValuesPerGrid, m_eclipseCase ); } //-------------------------------------------------------------------------------------------------- @@ -2109,7 +2054,7 @@ std::vector const RigActiveCellInfo* matrixActiveCellInfo, const RigActiveCellInfo* fractureActiveCellInfo, RiaDefines::PorosityModelType porosityModel, - size_t timeStepCount ) const + size_t timeStepCount ) { CVF_ASSERT( matrixActiveCellInfo ); diff --git a/ApplicationLibCode/FileInterface/RifReaderEclipseOutput.h b/ApplicationLibCode/FileInterface/RifReaderEclipseOutput.h index 53279b524d..7984fc88b7 100644 --- a/ApplicationLibCode/FileInterface/RifReaderEclipseOutput.h +++ b/ApplicationLibCode/FileInterface/RifReaderEclipseOutput.h @@ -90,6 +90,7 @@ public: private: bool readActiveCellInfo(); + void buildMetaData( ecl_grid_type* grid ); void readWellCells( const ecl_grid_type* mainEclGrid, bool importCompleteMswData ); @@ -114,11 +115,11 @@ private: void ensureDynamicResultAccessIsPresent(); - std::vector validKeywordsForPorosityModel( const std::vector& keywordItemCounts, - const RigActiveCellInfo* activeCellInfo, - const RigActiveCellInfo* fractureActiveCellInfo, - RiaDefines::PorosityModelType matrixOrFracture, - size_t timeStepCount ) const; + static std::vector validKeywordsForPorosityModel( const std::vector& keywordItemCounts, + const RigActiveCellInfo* activeCellInfo, + const RigActiveCellInfo* fractureActiveCellInfo, + RiaDefines::PorosityModelType matrixOrFracture, + size_t timeStepCount ); std::vector createFilteredTimeStepInfos(); diff --git a/ApplicationLibCode/FileInterface/RifReaderOpmCommon.cpp b/ApplicationLibCode/FileInterface/RifReaderOpmCommon.cpp new file mode 100644 index 0000000000..546f7c21bd --- /dev/null +++ b/ApplicationLibCode/FileInterface/RifReaderOpmCommon.cpp @@ -0,0 +1,470 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2023- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RifReaderOpmCommon.h" + +#include "RiaEclipseFileNameTools.h" +#include "RiaLogging.h" +#include "RiaQDateTimeTools.h" + +#include "RifEclipseOutputFileTools.h" +#include "RifOpmGridTools.h" + +#include "RigEclipseCaseData.h" +#include "RigEclipseResultInfo.h" +#include "RigMainGrid.h" +#include "RigSimWellData.h" +#include "RigWellResultFrame.h" + +#include "opm/input/eclipse/Deck/Deck.hpp" +#include "opm/input/eclipse/EclipseState/Runspec.hpp" +#include "opm/input/eclipse/Parser/Parser.hpp" +#include "opm/input/eclipse/Schedule/Well/Connection.hpp" +#include "opm/input/eclipse/Schedule/Well/Well.hpp" +#include "opm/io/eclipse/EInit.hpp" +#include "opm/io/eclipse/ERst.hpp" +#include "opm/io/eclipse/RestartFileView.hpp" +#include "opm/io/eclipse/rst/state.hpp" +#include "opm/output/eclipse/VectorItems/group.hpp" +#include "opm/output/eclipse/VectorItems/well.hpp" + +using namespace Opm; + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifReaderOpmCommon::RifReaderOpmCommon() +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifReaderOpmCommon::~RifReaderOpmCommon() +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifReaderOpmCommon::open( const QString& fileName, RigEclipseCaseData* eclipseCase ) +{ + QStringList fileSet; + if ( !RifEclipseOutputFileTools::findSiblingFilesWithSameBaseName( fileName, &fileSet ) ) return false; + + try + { + m_gridFileName = fileName.toStdString(); + + if ( !RifOpmGridTools::importGrid( m_gridFileName, eclipseCase->mainGrid(), eclipseCase ) ) + { + RiaLogging::error( "Failed to open grid file " + fileName ); + + return false; + } + + buildMetaData( eclipseCase ); + + return true; + } + catch ( std::exception& e ) + { + auto description = e.what(); + RiaLogging::error( description ); + } + + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifReaderOpmCommon::staticResult( const QString& result, RiaDefines::PorosityModelType matrixOrFracture, std::vector* values ) +{ + if ( m_initFile ) + { + try + { + auto resultName = result.toStdString(); + + auto resultEntries = m_initFile->getList(); + for ( const auto& entry : resultEntries ) + { + const auto& [keyword, kwType, size] = entry; + if ( keyword == resultName ) + { + if ( kwType == EclIO::eclArrType::REAL ) + { + auto fileValues = m_initFile->getInitData( resultName ); + values->insert( values->end(), fileValues.begin(), fileValues.end() ); + } + else if ( kwType == EclIO::eclArrType::DOUB ) + { + auto fileValues = m_initFile->getInitData( resultName ); + values->insert( values->end(), fileValues.begin(), fileValues.end() ); + } + else if ( kwType == EclIO::eclArrType::INTE ) + { + auto fileValues = m_initFile->getInitData( resultName ); + values->insert( values->end(), fileValues.begin(), fileValues.end() ); + } + } + } + return true; + } + catch ( std::exception& e ) + { + RiaLogging::error( e.what() ); + } + } + + return false; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifReaderOpmCommon::dynamicResult( const QString& result, + RiaDefines::PorosityModelType matrixOrFracture, + size_t stepIndex, + std::vector* values ) +{ + if ( m_restartFile ) + { + try + { + auto resultName = result.toStdString(); + + auto stepNumbers = m_restartFile->listOfReportStepNumbers(); + auto stepNumber = stepNumbers[stepIndex]; + + auto resultEntries = m_restartFile->getList(); + for ( const auto& entry : resultEntries ) + { + const auto& [keyword, kwType, size] = entry; + if ( keyword == resultName ) + { + if ( kwType == EclIO::eclArrType::DOUB ) + { + auto fileValues = m_restartFile->getRestartData( resultName, stepNumber ); + values->insert( values->end(), fileValues.begin(), fileValues.end() ); + } + if ( kwType == EclIO::eclArrType::REAL ) + { + auto fileValues = m_restartFile->getRestartData( resultName, stepNumber ); + values->insert( values->end(), fileValues.begin(), fileValues.end() ); + } + else if ( kwType == EclIO::eclArrType::INTE ) + { + auto fileValues = m_restartFile->getRestartData( resultName, stepNumber ); + values->insert( values->end(), fileValues.begin(), fileValues.end() ); + } + } + } + + return true; + } + catch ( std::exception& e ) + { + RiaLogging::error( e.what() ); + } + } + + return false; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector createKeywordInfo( std::vector entries ) +{ + std::vector fileKeywordInfo; + + for ( const auto& entry : entries ) + { + const auto& [keyword, kwType, size] = entry; + + RifKeywordValueCount::KeywordDataType dataType = RifKeywordValueCount::KeywordDataType::UNKNOWN; + + if ( kwType == EclIO::eclArrType::INTE ) + dataType = RifKeywordValueCount::KeywordDataType::INTEGER; + else if ( kwType == EclIO::eclArrType::REAL ) + dataType = RifKeywordValueCount::KeywordDataType::FLOAT; + else if ( kwType == EclIO::eclArrType::DOUB ) + dataType = RifKeywordValueCount::KeywordDataType::DOUBLE; + + if ( dataType != RifKeywordValueCount::KeywordDataType::UNKNOWN ) + { + fileKeywordInfo.emplace_back( RifKeywordValueCount( keyword, static_cast( size ), dataType ) ); + } + } + + return fileKeywordInfo; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RifReaderOpmCommon::buildMetaData( RigEclipseCaseData* eclipseCase ) +{ + auto getFileNameForType = []( RiaEclipseFileNameTools::EclipseFileType fileType, const QString& candidate ) -> std::string + { + const QString ext = caf::AppEnum::text( fileType ); + if ( candidate.endsWith( ext, Qt::CaseInsensitive ) ) return candidate.toStdString(); + return {}; + }; + + std::string initFileName; + std::string restartFileName; + + QStringList fileSet; + RifEclipseOutputFileTools::findSiblingFilesWithSameBaseName( QString::fromStdString( m_gridFileName ), &fileSet ); + for ( const auto& s : fileSet ) + { + auto initCandidate = getFileNameForType( RiaEclipseFileNameTools::EclipseFileType::ECLIPSE_INIT, s ); + auto restartCandidate = getFileNameForType( RiaEclipseFileNameTools::EclipseFileType::ECLIPSE_UNRST, s ); + + if ( !initCandidate.empty() ) initFileName = initCandidate; + if ( !restartCandidate.empty() ) restartFileName = restartCandidate; + } + + RigEclipseTimeStepInfo firstTimeStepInfo{ QDateTime(), 0, 0.0 }; + if ( !restartFileName.empty() ) + { + m_restartFile = std::make_shared( restartFileName ); + + std::vector entries; + for ( auto reportNumber : m_restartFile->listOfReportStepNumbers() ) + { + auto reportEntries = m_restartFile->listOfRstArrays( reportNumber ); + entries.insert( entries.end(), reportEntries.begin(), reportEntries.end() ); + } + + auto timeStepsFromFile = readTimeSteps( m_restartFile ); + + std::vector timeStepInfos; + std::vector dateTimes; + for ( const auto& timeStep : timeStepsFromFile ) + { + QDate date( timeStep.year, timeStep.month, timeStep.day ); + QDateTime dateTime = RiaQDateTimeTools::createDateTime( date ); + dateTimes.push_back( dateTime ); + timeStepInfos.emplace_back( dateTime, timeStep.sequenceNumber, 0.0 ); + } + m_timeSteps = dateTimes; + + auto last = std::unique( entries.begin(), entries.end() ); + entries.erase( last, entries.end() ); + + std::vector keywordInfo = createKeywordInfo( entries ); + + RifEclipseOutputFileTools::createResultEntries( keywordInfo, timeStepInfos, RiaDefines::ResultCatType::DYNAMIC_NATIVE, eclipseCase ); + + firstTimeStepInfo = timeStepInfos.front(); + + readWellCells( m_restartFile, eclipseCase, m_timeSteps ); + } + + if ( !initFileName.empty() ) + { + m_initFile = std::make_unique( initFileName ); + + auto entries = m_initFile->list_arrays(); + std::vector keywordInfo = createKeywordInfo( entries ); + + RifEclipseOutputFileTools::createResultEntries( keywordInfo, { firstTimeStepInfo }, RiaDefines::ResultCatType::STATIC_NATIVE, eclipseCase ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RifReaderOpmCommon::readWellCells( std::shared_ptr restartFile, + RigEclipseCaseData* eclipseCase, + const std::vector& timeSteps ) +{ + // It is required to create a deck as the input parameter to runspec. The default() initialization of the runspec keyword does not + // initialize the object as expected. + + Deck deck; + Runspec runspec( deck ); + Parser parser( false ); + + cvf::Collection wells; + + try + { + if ( restartFile->numberOfReportSteps() != timeSteps.size() ) + { + RiaLogging::error( "Number of time steps is not matching number of report steps" ); + return; + } + + auto getWellStateAndNames = [&]() -> std::pair, std::set> + { + std::vector states; + std::set wellNames; + + for ( auto seqNumber : restartFile->listOfReportStepNumbers() ) + { + auto fileView = std::make_shared( restartFile, seqNumber ); + + auto state = RestartIO::RstState::load( fileView, runspec, parser ); + states.emplace_back( state ); + + for ( const auto& w : state.wells ) + { + wellNames.insert( w.name ); + } + } + + return std::make_pair( states, wellNames ); + }; + + auto [states, wellNames] = getWellStateAndNames(); + + for ( const auto& wellName : wellNames ) + { + cvf::ref simWellData = new RigSimWellData; + simWellData->setWellName( QString::fromStdString( wellName ) ); + simWellData->m_wellCellsTimeSteps.resize( timeSteps.size() ); + + for ( size_t timeIdx = 0; timeIdx < timeSteps.size(); ++timeIdx ) + { + auto state = states[timeIdx]; + + auto it = std::find_if( state.wells.begin(), + state.wells.end(), + [&wellName]( const RestartIO::RstWell& well ) { return well.name == wellName; } ); + if ( it == state.wells.end() ) continue; + + RestartIO::RstWell rstWell = *it; + + RigWellResultFrame& wellResFrame = simWellData->m_wellCellsTimeSteps[timeIdx]; + wellResFrame.setTimestamp( timeSteps[timeIdx] ); + + auto wellType = [&rstWell]() -> RiaDefines::WellProductionType + { + if ( rstWell.wtype.producer() ) return RiaDefines::WellProductionType::PRODUCER; + + if ( rstWell.wtype.injector_type() == InjectorType::WATER ) return RiaDefines::WellProductionType::WATER_INJECTOR; + if ( rstWell.wtype.injector_type() == InjectorType::GAS ) return RiaDefines::WellProductionType::GAS_INJECTOR; + if ( rstWell.wtype.injector_type() == InjectorType::OIL ) return RiaDefines::WellProductionType::OIL_INJECTOR; + + return RiaDefines::WellProductionType::UNDEFINED_PRODUCTION_TYPE; + }; + + wellResFrame.setProductionType( wellType() ); + + wellResFrame.setIsOpen( rstWell.well_status == RestartIO::Helpers::VectorItems::IWell::Value::Status::Open ); + + // Well head + RigWellResultPoint wellHead; + wellHead.setGridIndex( 0 ); + auto cellIndex = eclipseCase->mainGrid()->cellIndexFromIJK( rstWell.ij[0], rstWell.ij[1], 0 ); + wellHead.setGridCellIndex( cellIndex ); + + wellResFrame.setWellHead( wellHead ); + + // Grid cells + if ( !rstWell.connections.empty() ) + { + RigWellResultBranch wellResultBranch; + wellResultBranch.setErtBranchId( 0 ); // Normal wells have only one branch + + std::vector branchResultPoints = wellResultBranch.branchResultPoints(); + const size_t existingCellCount = branchResultPoints.size(); + branchResultPoints.reserve( existingCellCount + rstWell.connections.size() ); + + for ( const auto& conn : rstWell.connections ) + { + RigWellResultPoint wellResPoint; + wellResPoint.setGridIndex( 0 ); + auto cellIndex = eclipseCase->mainGrid()->cellIndexFromIJK( conn.ijk[0], conn.ijk[1], conn.ijk[2] ); + wellResPoint.setGridCellIndex( cellIndex ); + + wellResPoint.setIsOpen( conn.state == Connection::State::OPEN ); + + // All units for a Connection is given in SI units + // Convert back to the original units + // See RestartIO::RstConnection::RstConnection + auto us = state.unit_system; + wellResPoint.setFlowData( us.from_si( UnitSystem::measure::rate, conn.resv_rate ), + us.from_si( UnitSystem::measure::liquid_surface_rate, conn.oil_rate ), + us.from_si( UnitSystem::measure::gas_surface_rate, conn.gas_rate ), + us.from_si( UnitSystem::measure::liquid_surface_rate, conn.water_rate ) ); + wellResPoint.setConnectionFactor( us.from_si( UnitSystem::measure::transmissibility, conn.cf ) ); + + branchResultPoints.push_back( wellResPoint ); + } + wellResultBranch.setBranchResultPoints( branchResultPoints ); + wellResFrame.addWellResultBranch( wellResultBranch ); + } + } + + simWellData->computeMappingFromResultTimeIndicesToWellTimeIndices( timeSteps ); + wells.push_back( simWellData.p() ); + } + } + catch ( std::exception& e ) + { + std::cout << "Exception: " << e.what() << std::endl; + } + + eclipseCase->setSimWellData( wells ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RifReaderOpmCommon::readTimeSteps( std::shared_ptr restartFile ) +{ + // It is required to create a deck as the input parameter to runspec. The default() initialization of the runspec keyword does not + // initialize the object as expected. + + Deck deck; + Runspec runspec( deck ); + Parser parser( false ); + + std::vector reportTimeData; + try + { + for ( auto seqNumber : restartFile->listOfReportStepNumbers() ) + { + auto fileView = std::make_shared( restartFile, seqNumber ); + + auto state = RestartIO::RstState::load( fileView, runspec, parser ); + auto header = state.header; + + int year = header.year; + int month = header.month; + int day = header.mday; + + double daySinceSimStart = header.next_timestep1; + + reportTimeData.emplace_back( + TimeDataFile{ .sequenceNumber = seqNumber, .year = year, .month = month, .day = day, .simulationTimeFromStart = daySinceSimStart } ); + } + } + catch ( std::exception& e ) + { + std::cout << "Exception: " << e.what() << std::endl; + } + + return reportTimeData; +} diff --git a/ApplicationLibCode/FileInterface/RifReaderOpmCommon.h b/ApplicationLibCode/FileInterface/RifReaderOpmCommon.h new file mode 100644 index 0000000000..4f7bbb121b --- /dev/null +++ b/ApplicationLibCode/FileInterface/RifReaderOpmCommon.h @@ -0,0 +1,70 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2023- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "RifReaderInterface.h" + +#include + +namespace Opm::EclIO +{ +class EInit; +class ERst; +} // namespace Opm::EclIO + +//================================================================================================== +// +// +//================================================================================================== +class RifReaderOpmCommon : public RifReaderInterface +{ +public: + struct TimeDataFile + { + int sequenceNumber; + int year; + int month; + int day; + double simulationTimeFromStart; + }; + + RifReaderOpmCommon(); + ~RifReaderOpmCommon() override; + + bool open( const QString& fileName, RigEclipseCaseData* eclipseCase ) override; + + bool staticResult( const QString& result, RiaDefines::PorosityModelType matrixOrFracture, std::vector* values ) override; + bool dynamicResult( const QString& result, RiaDefines::PorosityModelType matrixOrFracture, size_t stepIndex, std::vector* values ) override; + +private: + void buildMetaData( RigEclipseCaseData* eclipseCase ); + + static std::vector readTimeSteps( std::shared_ptr restartFile ); + static void readWellCells( std::shared_ptr restartFile, + RigEclipseCaseData* eclipseCase, + const std::vector& timeSteps ); + +private: + std::string m_gridFileName; + + std::shared_ptr m_restartFile; + std::shared_ptr m_initFile; + + std::vector m_timeSteps; +}; diff --git a/ApplicationLibCode/ProjectDataModel/RimEclipseResultCase.cpp b/ApplicationLibCode/ProjectDataModel/RimEclipseResultCase.cpp index 1501faad59..b08c30af91 100644 --- a/ApplicationLibCode/ProjectDataModel/RimEclipseResultCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimEclipseResultCase.cpp @@ -34,6 +34,7 @@ #include "RifReaderEclipseOutput.h" #include "RifReaderEclipseRft.h" #include "RifReaderMockModel.h" +#include "RifReaderOpmCommon.h" #include "RifReaderOpmRft.h" #include "RifReaderSettings.h" @@ -77,6 +78,14 @@ RimEclipseResultCase::RimEclipseResultCase() { CAF_PDM_InitScriptableObject( "Eclipse Case", ":/Case48x48.png", "", "The Regular Eclipse Results Case" ); + auto defaultReader = RiaPreferences::current()->gridModelReader(); + CAF_PDM_InitField( &m_gridModelReader, "gridModelReader", caf::AppEnum( defaultReader ), "Grid Model Reader" ); + if ( !RiaApplication::enableDevelopmentFeatures() ) + { + m_gridModelReader.xmlCapability()->disableIO(); + m_gridModelReader.uiCapability()->setUiHidden( true ); + } + CAF_PDM_InitFieldNoDefault( &m_unitSystem, "UnitSystem", "Unit System" ); m_unitSystem.registerGetMethod( RimProject::current(), &RimProject::commonUnitSystemForAllCases ); m_unitSystem.uiCapability()->setUiReadOnly( true ); @@ -86,9 +95,7 @@ RimEclipseResultCase::RimEclipseResultCase() m_flowDiagSolutions.uiCapability()->setUiTreeChildrenHidden( true ); m_flipXAxis.xmlCapability()->setIOWritable( true ); - // flipXAxis.uiCapability()->setUiHidden(true); m_flipYAxis.xmlCapability()->setIOWritable( true ); - // flipYAxis.uiCapability()->setUiHidden(true); CAF_PDM_InitFieldNoDefault( &m_sourSimFileName, "SourSimFileName", "SourSim File Name" ); m_sourSimFileName.uiCapability()->setUiEditorTypeName( caf::PdmUiFilePathEditor::uiEditorTypeName() ); @@ -133,60 +140,68 @@ bool RimEclipseResultCase::importGridAndResultMetaData( bool showTimeStepFilter return false; } - cvf::ref readerEclipseOutput = new RifReaderEclipseOutput; - readerEclipseOutput->setFilenamesWithFaults( filesContainingFaults() ); - readerEclipseOutput->setReaderSettings( m_readerSettings ); - - cvf::ref restartDataAccess = RifEclipseOutputFileTools::createDynamicResultAccess( gridFileName() ); - + if ( m_gridModelReader == RiaDefines::GridModelReader::LIBECL ) { - std::vector timeSteps; - std::vector daysSinceSimulationStart; + auto readerEclipseOutput = new RifReaderEclipseOutput(); + + cvf::ref restartDataAccess = RifEclipseOutputFileTools::createDynamicResultAccess( gridFileName() ); - if ( restartDataAccess.notNull() ) { - restartDataAccess->timeSteps( &timeSteps, &daysSinceSimulationStart ); + std::vector timeSteps; + std::vector daysSinceSimulationStart; + + if ( restartDataAccess.notNull() ) + { + restartDataAccess->timeSteps( &timeSteps, &daysSinceSimulationStart ); + } + m_timeStepFilter->setTimeStepsFromFile( timeSteps ); } - m_timeStepFilter->setTimeStepsFromFile( timeSteps ); + + if ( showTimeStepFilter ) + { + caf::PdmUiPropertyViewDialog propertyDialog( nullptr, + m_timeStepFilter, + "Time Step Filter", + "", + QDialogButtonBox::Ok | QDialogButtonBox::Cancel ); + propertyDialog.resize( QSize( 400, 400 ) ); + + // Push arrow cursor onto the cursor stack so it takes over from the wait cursor. + QApplication::setOverrideCursor( QCursor( Qt::ArrowCursor ) ); + // Show GUI to select time steps + int dialogReturnValue = propertyDialog.exec(); + // Pop arrow cursor off the cursor stack so that the previous (wait) cursor takes over. + QApplication::restoreOverrideCursor(); + + if ( dialogReturnValue != QDialog::Accepted ) + { + return false; + } + m_timeStepFilter->updateFilteredTimeStepsFromUi(); + } + + readerEclipseOutput->setFileDataAccess( restartDataAccess.p() ); + readerEclipseOutput->setTimeStepFilter( m_timeStepFilter->filteredTimeSteps() ); + + readerInterface = readerEclipseOutput; + } + else + { + readerInterface = new RifReaderOpmCommon; } - if ( showTimeStepFilter ) - { - caf::PdmUiPropertyViewDialog propertyDialog( nullptr, - m_timeStepFilter, - "Time Step Filter", - "", - QDialogButtonBox::Ok | QDialogButtonBox::Cancel ); - propertyDialog.resize( QSize( 400, 400 ) ); - - // Push arrow cursor onto the cursor stack so it takes over from the wait cursor. - QApplication::setOverrideCursor( QCursor( Qt::ArrowCursor ) ); - // Show GUI to select time steps - int dialogReturnValue = propertyDialog.exec(); - // Pop arrow cursor off the cursor stack so that the previous (wait) cursor takes over. - QApplication::restoreOverrideCursor(); - - if ( dialogReturnValue != QDialog::Accepted ) - { - return false; - } - m_timeStepFilter->updateFilteredTimeStepsFromUi(); - } - - readerEclipseOutput->setFileDataAccess( restartDataAccess.p() ); - readerEclipseOutput->setTimeStepFilter( m_timeStepFilter->filteredTimeSteps() ); + readerInterface->setFilenamesWithFaults( filesContainingFaults() ); + readerInterface->setReaderSettings( m_readerSettings ); cvf::ref eclipseCase = new RigEclipseCaseData( this ); - if ( !readerEclipseOutput->open( gridFileName(), eclipseCase.p() ) ) + if ( !readerInterface->open( gridFileName(), eclipseCase.p() ) ) { return false; } - setFilesContainingFaults( readerEclipseOutput->filenamesWithFaults() ); + setFilesContainingFaults( readerInterface->filenamesWithFaults() ); setReservoirData( eclipseCase.p() ); - - readerInterface = readerEclipseOutput; } results( RiaDefines::PorosityModelType::MATRIX_MODEL )->setReaderInterface( readerInterface.p() ); @@ -581,6 +596,7 @@ bool RimEclipseResultCase::hasSourSimFile() //-------------------------------------------------------------------------------------------------- void RimEclipseResultCase::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { + uiOrdering.add( &m_gridModelReader ); uiOrdering.add( &m_caseUserDescription ); uiOrdering.add( &m_displayNameOption ); uiOrdering.add( &m_caseId ); diff --git a/ApplicationLibCode/ProjectDataModel/RimEclipseResultCase.h b/ApplicationLibCode/ProjectDataModel/RimEclipseResultCase.h index 34bcd6bf04..9edbd91b7d 100644 --- a/ApplicationLibCode/ProjectDataModel/RimEclipseResultCase.h +++ b/ApplicationLibCode/ProjectDataModel/RimEclipseResultCase.h @@ -81,17 +81,16 @@ private: void loadAndUpdateSourSimData(); void ensureRftDataIsImported(); -private: cvf::ref createMockModel( QString modelName ); + void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override; - void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override; - +private: cvf::ref m_flowDagSolverInterface; cvf::ref m_readerEclipseRft; cvf::ref m_readerOpmRft; - // Fields: + caf::PdmField> m_gridModelReader; caf::PdmProxyValueField> m_unitSystem; caf::PdmChildArrayField m_flowDiagSolutions; caf::PdmField m_sourSimFileName; diff --git a/ApplicationLibCode/ReservoirDataModel/ResultCalculators/RigCellVolumeResultCalculator.cpp b/ApplicationLibCode/ReservoirDataModel/ResultCalculators/RigCellVolumeResultCalculator.cpp index 471fcfc1a1..99ba1fcbb2 100644 --- a/ApplicationLibCode/ReservoirDataModel/ResultCalculators/RigCellVolumeResultCalculator.cpp +++ b/ApplicationLibCode/ReservoirDataModel/ResultCalculators/RigCellVolumeResultCalculator.cpp @@ -54,6 +54,11 @@ bool RigCellVolumeResultCalculator::isMatching( const RigEclipseResultAddress& r //-------------------------------------------------------------------------------------------------- void RigCellVolumeResultCalculator::calculate( const RigEclipseResultAddress& resVarAddr, size_t timeStepIndex ) { + if ( m_resultsData->activeCellInfo()->reservoirActiveCellCount() == 0 ) + { + return; + } + size_t cellVolIdx = m_resultsData->findOrCreateScalarResultIndex( RigEclipseResultAddress( RiaDefines::ResultCatType::STATIC_NATIVE, RiaResultNames::riCellVolumeResultName() ), false ); diff --git a/ApplicationLibCode/ReservoirDataModel/RigSimWellData.cpp b/ApplicationLibCode/ReservoirDataModel/RigSimWellData.cpp index fe6cffab24..2d51e9ad0b 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigSimWellData.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigSimWellData.cpp @@ -292,6 +292,14 @@ bool RigSimWellData::isMultiSegmentWell() const return m_isMultiSegmentWell; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RigSimWellData::setWellName( const QString& wellName ) +{ + m_wellName = wellName; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ReservoirDataModel/RigSimWellData.h b/ApplicationLibCode/ReservoirDataModel/RigSimWellData.h index 55aadc0554..5d82c2978f 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigSimWellData.h +++ b/ApplicationLibCode/ReservoirDataModel/RigSimWellData.h @@ -43,6 +43,8 @@ public: void setMultiSegmentWell( bool isMultiSegmentWell ); bool isMultiSegmentWell() const; + void setWellName( const QString& wellName ); + bool hasWellResult( size_t resultTimeStepIndex ) const; bool hasAnyValidCells( size_t resultTimeStepIndex ) const; diff --git a/ApplicationLibCode/UnitTests/CMakeLists_files.cmake b/ApplicationLibCode/UnitTests/CMakeLists_files.cmake index 67d65905c6..e22535f905 100644 --- a/ApplicationLibCode/UnitTests/CMakeLists_files.cmake +++ b/ApplicationLibCode/UnitTests/CMakeLists_files.cmake @@ -96,6 +96,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RimWellLogCalculatedCurve-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RigWellLogCurveData-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RiaNumericalTools-Test.cpp + ${CMAKE_CURRENT_LIST_DIR}/opm-import-well-data-Test.cpp ) if(RESINSIGHT_ENABLE_GRPC) diff --git a/ApplicationLibCode/UnitTests/opm-import-well-data-Test.cpp b/ApplicationLibCode/UnitTests/opm-import-well-data-Test.cpp new file mode 100644 index 0000000000..e8bb10ad88 --- /dev/null +++ b/ApplicationLibCode/UnitTests/opm-import-well-data-Test.cpp @@ -0,0 +1,54 @@ + +#include "RiaTestDataDirectory.h" +#include "gtest/gtest.h" + +#include "opm/input/eclipse/Deck/Deck.hpp" +#include "opm/input/eclipse/Parser/Parser.hpp" +#include "opm/io/eclipse/ERst.hpp" +#include "opm/io/eclipse/RestartFileView.hpp" +#include "opm/io/eclipse/rst/state.hpp" +#include "opm/io/eclipse/rst/well.hpp" + +#include +#include + +const std::string drogonPath = "e:/gitroot/resinsight-tutorials/model-data/drogon/DROGON-0.UNRST"; + +TEST( DISABLED_opm_well_data_test, TestImport ) +{ + Opm::Deck deck; + + // It is required to create a deck as the input parameter to runspec. The = default() initialization of the runspec keyword does not + // initialize the object as expected. + Opm::Runspec runspec( deck ); + Opm::Parser parser( false ); + + try + { + QDir baseFolder( TEST_MODEL_DIR ); + bool subFolderExists = baseFolder.cd( "TEST10K_FLT_LGR_NNC" ); + EXPECT_TRUE( subFolderExists ); + QString filename( "TEST10K_FLT_LGR_NNC.UNRST" ); + QString filePath = baseFolder.absoluteFilePath( filename ); + + auto stdFilename = baseFolder.absoluteFilePath( filename ).toStdString(); + + auto rstFile = std::make_shared( drogonPath ); + for ( auto seqNumber : rstFile->listOfReportStepNumbers() ) + { + auto fileView = std::make_shared( rstFile, seqNumber ); + + auto state = Opm::RestartIO::RstState::load( fileView, runspec, parser ); + + for ( const auto& w : state.wells ) + { + auto name = w.name; + std::cout << name << std::endl; + } + } + } + catch ( std::exception& e ) + { + std::cout << "Exception: " << e.what() << std::endl; + } +} diff --git a/CMakeLists.txt b/CMakeLists.txt index d39a477d85..57936de2a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -448,16 +448,19 @@ if((NOT RESINSIGHT_BUILD_LIBS_FROM_SOURCE) AND MSVC) URL https://github.com/CeetronSolutions/resinsight-dependencies/releases/download/2023.08/custom-opm-common.zip ) FetchContent_Populate(ri-dependencies) - + add_library(custom-opm-common STATIC IMPORTED) - set_target_properties(custom-opm-common PROPERTIES - IMPORTED_CONFIGURATIONS "Debug;Release" - IMPORTED_LOCATION_DEBUG "${ri-dependencies_SOURCE_DIR}/custom-opm-common_debug.lib" - IMPORTED_LOCATION_RELEASE "${ri-dependencies_SOURCE_DIR}/custom-opm-common.lib" - ) - + set_target_properties( + custom-opm-common + PROPERTIES IMPORTED_CONFIGURATIONS "Debug;Release" + IMPORTED_LOCATION_DEBUG + "${ri-dependencies_SOURCE_DIR}/custom-opm-common_debug.lib" + IMPORTED_LOCATION_RELEASE + "${ri-dependencies_SOURCE_DIR}/custom-opm-common.lib" + ) + list(APPEND EXTERNAL_LINK_LIBRARIES custom-opm-common) - + message(STATUS "opm-common: Enabled use of precompiled library") else() add_subdirectory(ThirdParty/custom-opm-common) diff --git a/ThirdParty/custom-opm-common/CMakeLists.txt b/ThirdParty/custom-opm-common/CMakeLists.txt index 33b54f09f2..5439d66b38 100644 --- a/ThirdParty/custom-opm-common/CMakeLists.txt +++ b/ThirdParty/custom-opm-common/CMakeLists.txt @@ -92,10 +92,21 @@ add_library(${PROJECT_NAME} opm-common/src/opm/io/eclipse/rst/connection.cpp # Required for use of RstHeader::restart_info + opm-common/src/opm/io/eclipse/rst/action.cpp + opm-common/src/opm/io/eclipse/rst/aquifer.cpp + opm-common/src/opm/io/eclipse/rst/connection.cpp + opm-common/src/opm/io/eclipse/rst/group.cpp opm-common/src/opm/io/eclipse/rst/header.cpp + opm-common/src/opm/io/eclipse/rst/network.cpp + opm-common/src/opm/io/eclipse/rst/segment.cpp + opm-common/src/opm/io/eclipse/rst/state.cpp + opm-common/src/opm/io/eclipse/rst/udq.cpp + opm-common/src/opm/io/eclipse/rst/well.cpp + opm-common/src/opm/output/eclipse/InteHEAD.cpp + opm-common/src/opm/output/eclipse/UDQDims.cpp + opm-common/src/opm/output/eclipse/CreateActionRSTDims.cpp # 2022.06 additional includes - opm-common/src/opm/io/eclipse/rst/aquifer.cpp opm-common/src/opm/io/eclipse/ERst.cpp opm-common/src/opm/io/eclipse/RestartFileView.cpp opm-common/cross-platform/windows/Substitutes.cpp