///////////////////////////////////////////////////////////////////////////////// // // 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 "RiaStdStringTools.h" #include "RifEclipseOutputFileTools.h" #include "RifEclipseReportKeywords.h" #include "RifEclipseUnifiedRestartFileAccess.h" #include "RifOpmRadialGridTools.h" #include "RifReaderEclipseWell.h" #include "RigActiveCellGrid.h" #include "RigActiveCellInfo.h" #include "RigCaseCellResultsData.h" #include "RigEclipseCaseData.h" #include "RigEclipseResultInfo.h" #include "RigMainGrid.h" #include "RigNNCData.h" #include "Well/RigSimWellData.h" #include "Well/RigWellResultFrame.h" #include "cafProgressInfo.h" #include "opm/io/eclipse/EGrid.hpp" #include "opm/io/eclipse/EInit.hpp" #include "opm/io/eclipse/ERst.hpp" #include "opm/output/eclipse/VectorItems/intehead.hpp" #include using namespace Opm; //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RifReaderOpmCommon::RifReaderOpmCommon() : m_eclipseCaseData( nullptr ) , m_gridUnit( -1 ) { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RifReaderOpmCommon::~RifReaderOpmCommon() { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RifReaderOpmCommon::open( const QString& fileName, RigEclipseCaseData* eclipseCaseData ) { caf::ProgressInfo progress( 100, "Reading Grid" ); QStringList fileSet; if ( !RifEclipseOutputFileTools::findSiblingFilesWithSameBaseName( fileName, &fileSet ) ) return false; try { m_gridFileName = fileName.toStdString(); locateInitAndRestartFiles( fileName ); if ( !importGrid( eclipseCaseData->mainGrid(), eclipseCaseData ) ) { RiaLogging::error( "Failed to open grid file " + fileName ); return false; } if ( isFaultImportEnabled() ) { auto task = progress.task( "Reading faults", 25 ); cvf::Collection faults; importFaults( fileSet, &faults ); RigMainGrid* mainGrid = eclipseCaseData->mainGrid(); mainGrid->setFaults( faults ); } m_eclipseCaseData = eclipseCaseData; { auto task = progress.task( "Reading Results Meta data", 25 ); buildMetaData( eclipseCaseData, progress ); } if ( isNNCsEnabled() ) { auto task = progress.task( "Handling NCC Result data", 25 ); caf::ProgressInfo nncProgress( 10, "" ); RigMainGrid* mainGrid = eclipseCaseData->mainGrid(); // This test should probably be improved to test more directly for presence of NNC data if ( eclipseCaseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->hasFlowDiagUsableFluxes() ) { auto subNncTask = nncProgress.task( "Reading dynamic NNC data" ); transferDynamicNNCData( mainGrid ); } RigActiveCellInfo* activeCellInfo = m_eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ); bool includeInactiveCells = includeInactiveCellsInFaultGeometry(); mainGrid->nncData()->setSourceDataForProcessing( mainGrid, activeCellInfo, includeInactiveCells ); } return true; } catch ( std::exception& e ) { auto description = e.what(); RiaLogging::error( description ); } return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RifReaderOpmCommon::importGrid( RigMainGrid* mainGrid, RigEclipseCaseData* eclipseCaseData ) { caf::ProgressInfo progInfo( 5, "Importing Eclipse Grid" ); Opm::EclIO::EGrid opmGrid( m_gridFileName ); const auto& dims = opmGrid.dimension(); mainGrid->setGridPointDimensions( cvf::Vec3st( dims[0] + 1, dims[1] + 1, dims[2] + 1 ) ); mainGrid->setGridName( "Main grid" ); mainGrid->setDualPorosity( opmGrid.porosity_mode() > 0 ); // assign grid unit, if found (1 = Metric, 2 = Field, 3 = Lab) auto gridUnitStr = RiaStdStringTools::toUpper( opmGrid.grid_unit() ); if ( gridUnitStr.starts_with( 'M' ) ) m_gridUnit = 1; else if ( gridUnitStr.starts_with( 'F' ) ) m_gridUnit = 2; else if ( gridUnitStr.starts_with( 'C' ) ) m_gridUnit = 3; auto totalCellCount = opmGrid.totalNumberOfCells(); auto globalMatrixActiveSize = opmGrid.activeCells(); auto globalFractureActiveSize = opmGrid.activeFracCells(); const auto& lgr_names = opmGrid.list_of_lgrs(); m_gridNames.clear(); m_gridNames.push_back( "global" ); m_gridNames.insert( m_gridNames.end(), lgr_names.begin(), lgr_names.end() ); const auto& lgr_parent_names = opmGrid.list_of_lgr_parents(); const int numLGRs = (int)lgr_names.size(); std::vector lgrGrids; for ( int lgrIdx = 0; lgrIdx < numLGRs; lgrIdx++ ) { lgrGrids.emplace_back( Opm::EclIO::EGrid( m_gridFileName, lgr_names[lgrIdx] ) ); RigLocalGrid* localGrid = new RigLocalGrid( mainGrid ); const auto& lgrDims = lgrGrids[lgrIdx].dimension(); localGrid->setGridPointDimensions( cvf::Vec3st( lgrDims[0] + 1, lgrDims[1] + 1, lgrDims[2] + 1 ) ); localGrid->setGridId( lgrIdx + 1 ); localGrid->setGridName( lgr_names[lgrIdx] ); localGrid->setIndexToStartOfCells( totalCellCount ); mainGrid->addLocalGrid( localGrid ); totalCellCount += lgrGrids[lgrIdx].totalNumberOfCells(); } // active cell information { RigActiveCellInfo* activeCellInfo = eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ); RigActiveCellInfo* fractureActiveCellInfo = eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL ); activeCellInfo->setReservoirCellCount( totalCellCount ); fractureActiveCellInfo->setReservoirCellCount( totalCellCount ); activeCellInfo->setGridCount( 1 + numLGRs ); fractureActiveCellInfo->setGridCount( 1 + numLGRs ); auto task = progInfo.task( "Getting Active Cell Information", 1 ); for ( int lgrIdx = 0; lgrIdx < numLGRs; lgrIdx++ ) { globalMatrixActiveSize += lgrGrids[lgrIdx].activeCells(); globalFractureActiveSize += lgrGrids[lgrIdx].activeFracCells(); } // in case init file and grid file disagrees with number of active cells, read extra porv information from init file to correct this if ( !verifyActiveCellInfo( globalMatrixActiveSize, globalFractureActiveSize ) ) { updateActiveCellInfo( eclipseCaseData, opmGrid, lgrGrids, mainGrid ); } globalMatrixActiveSize = opmGrid.activeCells(); globalFractureActiveSize = opmGrid.activeFracCells(); activeCellInfo->setGridActiveCellCounts( 0, globalMatrixActiveSize ); fractureActiveCellInfo->setGridActiveCellCounts( 0, globalFractureActiveSize ); transferActiveCells( opmGrid, 0, eclipseCaseData, 0, 0 ); size_t cellCount = opmGrid.totalNumberOfCells(); for ( int lgrIdx = 0; lgrIdx < numLGRs; lgrIdx++ ) { auto& lgrGrid = lgrGrids[lgrIdx]; transferActiveCells( lgrGrid, cellCount, eclipseCaseData, globalMatrixActiveSize, globalFractureActiveSize ); cellCount += lgrGrid.totalNumberOfCells(); globalMatrixActiveSize += lgrGrid.activeCells(); globalFractureActiveSize += lgrGrid.activeFracCells(); activeCellInfo->setGridActiveCellCounts( lgrIdx + 1, lgrGrid.activeCells() ); fractureActiveCellInfo->setGridActiveCellCounts( lgrIdx + 1, lgrGrid.activeFracCells() ); } activeCellInfo->computeDerivedData(); fractureActiveCellInfo->computeDerivedData(); } // grid geometry { auto task = progInfo.task( "Loading Main Grid Geometry", 1 ); transferGeometry( opmGrid, opmGrid, mainGrid, mainGrid, eclipseCaseData ); bool hasParentInfo = ( lgr_parent_names.size() >= (size_t)numLGRs ); auto task2 = progInfo.task( "Loading LGR Grid Geometry ", 1 ); for ( int lgrIdx = 0; lgrIdx < numLGRs; lgrIdx++ ) { RigGridBase* parentGrid = hasParentInfo ? mainGrid->gridByName( lgr_parent_names[lgrIdx] ) : mainGrid; RigLocalGrid* localGrid = static_cast( mainGrid->gridById( lgrIdx + 1 ) ); localGrid->setParentGrid( parentGrid ); transferGeometry( opmGrid, lgrGrids[lgrIdx], mainGrid, localGrid, eclipseCaseData ); } } mainGrid->initAllSubGridsParentGridPointer(); if ( isNNCsEnabled() ) { auto task = progInfo.task( "Loading NNC data", 1 ); transferStaticNNCData( opmGrid, lgrGrids, mainGrid ); } auto opmMapAxes = opmGrid.get_mapaxes(); if ( opmMapAxes.size() == 6 ) { std::array mapAxes; for ( size_t i = 0; i < opmMapAxes.size(); ++i ) { mapAxes[i] = opmMapAxes[i]; } double norm_denominator = mapAxes[2] * mapAxes[5] - mapAxes[4] * mapAxes[3]; // Set the map axes transformation matrix on the main grid mainGrid->setMapAxes( mapAxes ); mainGrid->setUseMapAxes( norm_denominator != 0.0 ); auto transform = mainGrid->mapAxisTransform(); // Invert the transformation matrix to convert from file coordinates to domain coordinates transform.invert(); #pragma omp parallel for for ( long i = 0; i < static_cast( mainGrid->nodes().size() ); i++ ) { auto& n = mainGrid->nodes()[i]; n.transformPoint( transform ); } } return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifReaderOpmCommon::transferStaticNNCData( Opm::EclIO::EGrid& opmMainGrid, std::vector& lgrGrids, RigMainGrid* mainGrid ) { opmMainGrid.load_nnc_data(); for ( auto& lgr : lgrGrids ) { lgr.load_nnc_data(); } auto connections = opmMainGrid.nnc_connections( 0 ); for ( int i = 0; i < (int)lgrGrids.size(); i++ ) { auto conn = lgrGrids[i].nnc_connections( i + 1 ); connections.insert( connections.end(), conn.begin(), conn.end() ); } if ( !connections.empty() ) { // Transform to our own data structures RigConnectionContainer nncConnections; std::vector transmissibilityValues; for ( auto& c : connections ) { RigGridBase* grid1 = mainGrid->gridByIndex( c.grid1_Id ); RigGridBase* grid2 = mainGrid->gridByIndex( c.grid2_Id ); RigConnection nncConnection( grid1->reservoirCellIndex( c.grid1_CellIdx - 1 ), grid2->reservoirCellIndex( c.grid2_CellIdx - 1 ) ); nncConnections.push_back( nncConnection ); transmissibilityValues.push_back( c.transValue ); } mainGrid->nncData()->setEclipseConnections( nncConnections ); mainGrid->nncData()->makeScalarResultAndSetValues( RiaDefines::propertyNameCombTrans(), transmissibilityValues ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifReaderOpmCommon::transferDynamicNNCData( RigMainGrid* mainGrid ) { if ( !m_restartFile ) return; const size_t timeStepCount = m_restartFile->numberOfReportSteps(); std::vector>& waterFluxData = mainGrid->nncData()->makeDynamicConnectionScalarResult( RiaDefines::propertyNameFluxWat(), timeStepCount ); std::vector>& oilFluxData = mainGrid->nncData()->makeDynamicConnectionScalarResult( RiaDefines::propertyNameFluxOil(), timeStepCount ); std::vector>& gasFluxData = mainGrid->nncData()->makeDynamicConnectionScalarResult( RiaDefines::propertyNameFluxGas(), timeStepCount ); for ( size_t timeStep = 0; timeStep < timeStepCount; ++timeStep ) { dynamicResult( "FLRWATN+", RiaDefines::PorosityModelType::MATRIX_MODEL, timeStep, &waterFluxData[timeStep] ); dynamicResult( "FLRGASN+", RiaDefines::PorosityModelType::MATRIX_MODEL, timeStep, &gasFluxData[timeStep] ); dynamicResult( "FLROILN+", RiaDefines::PorosityModelType::MATRIX_MODEL, timeStep, &oilFluxData[timeStep] ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifReaderOpmCommon::transferActiveCells( Opm::EclIO::EGrid& opmGrid, size_t cellStartIndex, RigEclipseCaseData* eclipseCaseData, size_t matrixActiveStartIndex, size_t fractureActiveStartIndex ) { const int cellCount = opmGrid.totalNumberOfCells(); RigActiveCellInfo* activeCellInfo = eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ); RigActiveCellInfo* fractureActiveCellInfo = eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL ); const auto& active_indexes = opmGrid.active_indexes(); const auto& active_frac_indexes = opmGrid.active_frac_indexes(); #pragma omp parallel for for ( int opmCellIndex = 0; opmCellIndex < (int)cellCount; opmCellIndex++ ) { // active cell index int matrixActiveIndex = active_indexes[opmCellIndex]; if ( matrixActiveIndex != -1 ) { activeCellInfo->setCellResultIndex( cellStartIndex + opmCellIndex, matrixActiveStartIndex + matrixActiveIndex ); } int fractureActiveIndex = active_frac_indexes[opmCellIndex]; if ( fractureActiveIndex != -1 ) { fractureActiveCellInfo->setCellResultIndex( cellStartIndex + opmCellIndex, fractureActiveStartIndex + fractureActiveIndex ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifReaderOpmCommon::transferGeometry( Opm::EclIO::EGrid& opmMainGrid, Opm::EclIO::EGrid& opmGrid, RigMainGrid* mainGrid, RigGridBase* localGrid, RigEclipseCaseData* eclipseCaseData ) { int cellCount = opmGrid.totalNumberOfCells(); size_t cellStartIndex = mainGrid->reservoirCells().size(); size_t nodeStartIndex = mainGrid->nodes().size(); const bool invalidateLongPyramidCells = invalidateLongThinCells(); RigCell defaultCell; defaultCell.setHostGrid( localGrid ); mainGrid->reservoirCells().resize( cellStartIndex + cellCount, defaultCell ); mainGrid->nodes().resize( nodeStartIndex + cellCount * 8, cvf::Vec3d( 0, 0, 0 ) ); auto& riNodes = mainGrid->nodes(); opmGrid.loadData(); opmGrid.load_grid_data(); const bool isRadialGrid = opmGrid.is_radial(); const auto& gridDimension = opmGrid.dimension(); const auto& hostCellGlobalIndices = opmGrid.hostCellsGlobalIndex(); // Compute the center of the LGR radial grid cells for each K layer auto radialGridCenterTopLayerOpm = isRadialGrid ? RifOpmRadialGridTools::computeXyCenterForTopOfCells( opmMainGrid, opmGrid, localGrid ) : std::map>(); // use same mapping as resdata const size_t cellMappingECLRi[8] = { 0, 1, 3, 2, 4, 5, 7, 6 }; #pragma omp parallel for for ( int opmCellIndex = 0; opmCellIndex < static_cast( localGrid->cellCount() ); opmCellIndex++ ) { auto opmIJK = opmGrid.ijk_from_global_index( opmCellIndex ); double xCenterCoordOpm = 0.0; double yCenterCoordOpm = 0.0; if ( isRadialGrid && radialGridCenterTopLayerOpm.contains( opmIJK[2] ) ) { const auto& [xCenter, yCenter] = radialGridCenterTopLayerOpm[opmIJK[2]]; xCenterCoordOpm = xCenter; yCenterCoordOpm = yCenter; } auto riReservoirIndex = localGrid->cellIndexFromIJK( opmIJK[0], opmIJK[1], opmIJK[2] ); RigCell& cell = mainGrid->cell( cellStartIndex + riReservoirIndex ); cell.setGridLocalCellIndex( riReservoirIndex ); // parent cell index if ( ( hostCellGlobalIndices.size() > (size_t)opmCellIndex ) && hostCellGlobalIndices[opmCellIndex] >= 0 ) { cell.setParentCellIndex( hostCellGlobalIndices[opmCellIndex] ); } else { cell.setParentCellIndex( cvf::UNDEFINED_SIZE_T ); } // corner coordinates 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 = nodeStartIndex + 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] + xCenterCoordOpm; riNode.y() = opmY[opmNodeIndex] + yCenterCoordOpm; riNode.z() = -opmZ[opmNodeIndex]; cell.cornerIndices()[riCornerIndex] = riNodeIndex; // First grid dimension is radius, check if cell are at the outer-most slice if ( isRadialGrid && !hostCellGlobalIndices.empty() && ( gridDimension[0] - 1 == opmIJK[0] ) ) { auto hostCellIndex = hostCellGlobalIndices[opmCellIndex]; RifOpmRadialGridTools::lockToHostPillars( riNode, opmMainGrid, opmGrid, opmIJK, hostCellIndex, opmCellIndex, opmNodeIndex, xCenterCoordOpm, yCenterCoordOpm ); } } if ( invalidateLongPyramidCells ) { cell.setInvalid( cell.isLongPyramidCell() ); } } // subgrid pointers RigLocalGrid* realLocalGrid = dynamic_cast( localGrid ); RigGridBase* parentGrid = realLocalGrid != nullptr ? realLocalGrid->parentGrid() : nullptr; if ( parentGrid != nullptr ) { for ( auto localCellInGlobalIdx : hostCellGlobalIndices ) { parentGrid->cell( localCellInGlobalIdx ).setSubGrid( realLocalGrid ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RifReaderOpmCommon::staticResult( const QString& result, RiaDefines::PorosityModelType matrixOrFracture, std::vector* values ) { if ( m_initFile ) { try { auto resultName = result.toStdString(); std::vector combinedFileValues; const auto& resultEntries = m_initFile->getList(); for ( const auto& entry : resultEntries ) { const auto& [keyword, kwType, size] = entry; if ( keyword == resultName ) { for ( auto& gridName : m_gridNames ) { if ( kwType == EclIO::eclArrType::REAL ) { const auto& fileValues = m_initFile->getInitData( resultName, gridName ); combinedFileValues.insert( combinedFileValues.end(), fileValues.begin(), fileValues.end() ); } else if ( kwType == EclIO::eclArrType::DOUB ) { const auto& fileValues = m_initFile->getInitData( resultName, gridName ); combinedFileValues.insert( combinedFileValues.end(), fileValues.begin(), fileValues.end() ); } else if ( kwType == EclIO::eclArrType::INTE ) { const auto& fileValues = m_initFile->getInitData( resultName, gridName ); combinedFileValues.insert( combinedFileValues.end(), fileValues.begin(), fileValues.end() ); } } break; } } // Always clear data after reading to avoid memory use m_initFile->clearData(); RifEclipseOutputFileTools::extractResultValuesBasedOnPorosityModel( m_eclipseCaseData, matrixOrFracture, values, combinedFileValues ); 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(); size_t indexOnFile = timeStepIndexOnFile( stepIndex ); const auto& stepNumbers = m_restartFile->listOfReportStepNumbers(); auto stepNumber = stepNumbers[indexOnFile]; std::vector combinedFileValues; auto resultEntries = m_restartFile->getList(); for ( const auto& entry : resultEntries ) { const auto& [keyword, kwType, size] = entry; if ( keyword == resultName ) { for ( auto& gridName : m_gridNames ) { if ( gridName == "global" ) // main grid, need to use separate method due to inner workings in opm_common { if ( kwType == EclIO::eclArrType::DOUB ) { const auto& fileValues = m_restartFile->getRestartData( resultName, stepNumber ); combinedFileValues.insert( combinedFileValues.end(), fileValues.begin(), fileValues.end() ); } if ( kwType == EclIO::eclArrType::REAL ) { const auto& fileValues = m_restartFile->getRestartData( resultName, stepNumber ); combinedFileValues.insert( combinedFileValues.end(), fileValues.begin(), fileValues.end() ); } else if ( kwType == EclIO::eclArrType::INTE ) { const auto& fileValues = m_restartFile->getRestartData( resultName, stepNumber ); combinedFileValues.insert( combinedFileValues.end(), fileValues.begin(), fileValues.end() ); } } else { if ( kwType == EclIO::eclArrType::DOUB ) { const auto& fileValues = m_restartFile->getRestartData( resultName, stepNumber, gridName ); combinedFileValues.insert( combinedFileValues.end(), fileValues.begin(), fileValues.end() ); } if ( kwType == EclIO::eclArrType::REAL ) { const auto& fileValues = m_restartFile->getRestartData( resultName, stepNumber, gridName ); combinedFileValues.insert( combinedFileValues.end(), fileValues.begin(), fileValues.end() ); } else if ( kwType == EclIO::eclArrType::INTE ) { const auto& fileValues = m_restartFile->getRestartData( resultName, stepNumber, gridName ); combinedFileValues.insert( combinedFileValues.end(), fileValues.begin(), fileValues.end() ); } } } break; } } // Always clear data after reading to avoid memory use m_restartFile->clearData(); RifEclipseOutputFileTools::extractResultValuesBasedOnPorosityModel( m_eclipseCaseData, matrixOrFracture, values, combinedFileValues ); return true; } catch ( std::exception& e ) { RiaLogging::error( e.what() ); } } return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- static std::vector createKeywordInfo( std::vector entries ) { RifEclipseReportKeywords keywordsReport; for ( const auto& entry : entries ) { const auto& [keyword, kwType, size] = entry; RifEclipseKeywordValueCount::KeywordDataType dataType = RifEclipseKeywordValueCount::KeywordDataType::UNKNOWN; if ( kwType == EclIO::eclArrType::INTE ) dataType = RifEclipseKeywordValueCount::KeywordDataType::INTEGER; else if ( kwType == EclIO::eclArrType::REAL ) dataType = RifEclipseKeywordValueCount::KeywordDataType::FLOAT; else if ( kwType == EclIO::eclArrType::DOUB ) dataType = RifEclipseKeywordValueCount::KeywordDataType::DOUBLE; if ( dataType != RifEclipseKeywordValueCount::KeywordDataType::UNKNOWN ) { keywordsReport.appendKeywordCount( keyword, static_cast( size ), dataType ); } } return keywordsReport.keywordValueCounts(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifReaderOpmCommon::locateInitAndRestartFiles( QString gridFileName ) { 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 {}; }; if ( m_initFileName.empty() || m_restartFileName.empty() ) { QStringList fileSet; RifEclipseOutputFileTools::findSiblingFilesWithSameBaseName( 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() ) m_initFileName = initCandidate; if ( !restartCandidate.empty() ) m_restartFileName = restartCandidate; } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifReaderOpmCommon::setupInitAndRestartAccess() { if ( ( m_initFile == nullptr ) && !m_initFileName.empty() ) { try { m_initFile = std::make_unique( m_initFileName ); } catch ( ... ) { m_initFile = nullptr; } } if ( ( m_restartFile == nullptr ) && !m_restartFileName.empty() ) { try { RiaLogging::resetTimer( "Starting import of meta data from " + QString::fromStdString( m_restartFileName ) ); m_restartFile = std::make_unique( m_restartFileName ); RiaLogging::logTimeElapsed( "Completed import of meta data" ); } catch ( ... ) { m_restartFile = nullptr; } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RifReaderOpmCommon::createFilteredTimeStepInfos() { std::vector timeStepInfos; if ( m_restartFile == nullptr ) return timeStepInfos; auto timeStepsOnFile = readTimeSteps(); if ( timeStepsOnFile.size() == 0 ) return timeStepInfos; auto startDayOffset = timeStepsOnFile[0].simulationTimeFromStart; QDate startDate( timeStepsOnFile[0].year, timeStepsOnFile[0].month, timeStepsOnFile[0].day ); for ( size_t i = 0; i < timeStepsOnFile.size(); i++ ) { if ( isTimeStepIncludedByFilter( i ) ) { auto dateTime = dateTimeFromTimeStepOnFile( timeStepsOnFile[i], startDate, startDayOffset ); timeStepInfos.push_back( RigEclipseTimeStepInfo( dateTime, timeStepsOnFile[i].sequenceNumber, timeStepsOnFile[i].simulationTimeFromStart ) ); } } return timeStepInfos; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QDateTime RifReaderOpmCommon::dateTimeFromTimeStepOnFile( RifReaderOpmCommon::TimeDataFile timeOnFile, QDate startDate, double startDayOffset ) { QDateTime dateTime; if ( timeOnFile.simulationTimeFromStart == 0 ) { QDate date( timeOnFile.year, timeOnFile.month, timeOnFile.day ); dateTime = RiaQDateTimeTools::createDateTime( date, Qt::TimeSpec::UTC ); } else { dateTime = RiaQDateTimeTools::createDateTime( startDate, Qt::TimeSpec::UTC ); double dayDoubleValue = timeOnFile.simulationTimeFromStart; int dayValue = cvf::Math::floor( dayDoubleValue ); const int adjustedDayValue = dayValue - startDayOffset; dateTime = dateTime.addDays( adjustedDayValue ); double dayFraction = dayDoubleValue - dayValue; double milliseconds = dayFraction * 24.0 * 60.0 * 60.0 * 1000.0; dateTime = dateTime.addMSecs( milliseconds ); } return dateTime; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifReaderOpmCommon::buildMetaData( RigEclipseCaseData* eclipseCaseData, caf::ProgressInfo& progress ) { setupInitAndRestartAccess(); std::vector filteredTimeStepInfos; RigEclipseTimeStepInfo firstTimeStepInfo{ QDateTime(), 0, 0.0 }; if ( m_restartFile != nullptr ) { std::vector entries; for ( auto reportNumber : m_restartFile->listOfReportStepNumbers() ) { auto stepEntries = m_restartFile->listOfRstArrays( reportNumber ); std::set> keyNames; for ( auto& [keyName, resType, nValues] : stepEntries ) { keyNames.insert( { keyName, resType } ); } for ( auto& [keyName, resType] : keyNames ) { auto dataSize = m_restartFile->dataSize( keyName, reportNumber ); entries.emplace_back( keyName, resType, dataSize ); } } auto last = std::unique( entries.begin(), entries.end() ); entries.erase( last, entries.end() ); std::vector keywordInfo = createKeywordInfo( entries ); filteredTimeStepInfos = createFilteredTimeStepInfos(); RifEclipseOutputFileTools::createResultEntries( keywordInfo, filteredTimeStepInfos, RiaDefines::ResultCatType::DYNAMIC_NATIVE, eclipseCaseData, m_restartFile->numberOfReportSteps() ); if ( !filteredTimeStepInfos.empty() ) firstTimeStepInfo = filteredTimeStepInfos.front(); } if ( m_initFile != nullptr ) { // entries from main grid auto entries = m_initFile->list_arrays(); // add lgr entries, too auto nGrids = m_gridNames.size(); for ( size_t i = 1; i < nGrids; i++ ) { auto gridEntries = m_initFile->list_arrays( m_gridNames[i] ); entries.insert( entries.end(), gridEntries.begin(), gridEntries.end() ); } std::vector keywordInfo = createKeywordInfo( entries ); RifEclipseOutputFileTools::createResultEntries( keywordInfo, { firstTimeStepInfo }, RiaDefines::ResultCatType::STATIC_NATIVE, eclipseCaseData, 1 ); } // Unit system { // Default units type is METRIC, look in restart file, then init file and then grid file until we find something RiaDefines::EclipseUnitSystem unitsType = RiaDefines::EclipseUnitSystem::UNITS_METRIC; int unitsTypeValue = -1; if ( m_restartFile != nullptr ) { if ( m_restartFile->hasArray( "INTEHEAD", 0 ) ) { const auto& intHeader = m_restartFile->getRestartData( "INTEHEAD", 0 ); if ( intHeader.size() > 2 ) unitsTypeValue = intHeader[2]; } } if ( unitsTypeValue < 0 ) { if ( m_initFile != nullptr ) { const auto& intHeader = m_initFile->getInitData( "INTEHEAD" ); if ( intHeader.size() > 2 ) unitsTypeValue = intHeader[2]; } } if ( unitsTypeValue < 0 ) { unitsTypeValue = m_gridUnit; } if ( unitsTypeValue == 2 ) { unitsType = RiaDefines::EclipseUnitSystem::UNITS_FIELD; } else if ( unitsTypeValue == 3 ) { unitsType = RiaDefines::EclipseUnitSystem::UNITS_LAB; } m_eclipseCaseData->setUnitsType( unitsType ); } auto task = progress.task( "Handling well information", 10 ); if ( loadWellDataEnabled() && !m_restartFileName.empty() ) { RiaLogging::resetTimer( "Start import of simulation well data" ); auto restartAccess = std::make_unique(); restartAccess->setRestartFiles( QStringList( QString::fromStdString( m_restartFileName ) ) ); restartAccess->open(); std::vector filteredTimeSteps; for ( auto& a : filteredTimeStepInfos ) { filteredTimeSteps.push_back( a.m_date ); } RifReaderEclipseWell::readWellCells( restartAccess.get(), eclipseCaseData, filteredTimeSteps, m_gridNames, isImportOfCompleteMswDataEnabled() ); restartAccess->close(); RiaLogging::logTimeElapsed( "Completed import of simulation well data" ); } else { RiaLogging::info( "Skipping import of simulation well data" ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RifReaderOpmCommon::readTimeSteps() { setupInitAndRestartAccess(); std::vector reportTimeData; if ( m_restartFile == nullptr ) return reportTimeData; try { namespace VI = Opm::RestartIO::Helpers::VectorItems; for ( auto seqNum : m_restartFile->listOfReportStepNumbers() ) { const std::string inteheadString = "INTEHEAD"; const std::string doubheadString = "DOUBHEAD"; if ( m_restartFile->hasArray( inteheadString, seqNum ) ) { const auto& intehead = m_restartFile->getRestartData( inteheadString, seqNum ); auto year = intehead[VI::intehead::YEAR]; auto month = intehead[VI::intehead::MONTH]; auto day = intehead[VI::intehead::DAY]; double daySinceSimStart = 0.0; if ( m_restartFile->hasArray( doubheadString, seqNum ) ) { const auto& doubhead = m_restartFile->getRestartData( doubheadString, seqNum ); if ( !doubhead.empty() ) { // Read out the simulation time from start from DOUBHEAD. There is no enum defined to access this value. // https://github.com/OPM/ResInsight/issues/11092 daySinceSimStart = doubhead[0]; } } reportTimeData.emplace_back( TimeDataFile{ .sequenceNumber = seqNum, .year = year, .month = month, .day = day, .simulationTimeFromStart = daySinceSimStart } ); } } } catch ( std::exception& e ) { std::cout << "Exception: " << e.what() << std::endl; } return reportTimeData; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RifReaderOpmCommon::timeStepsOnFile( QString gridFileName ) { locateInitAndRestartFiles( gridFileName ); setupInitAndRestartAccess(); if ( m_restartFile == nullptr ) return {}; auto timeStepsOnFile = readTimeSteps(); auto startDayOffset = timeStepsOnFile[0].simulationTimeFromStart; QDate startDate( timeStepsOnFile[0].year, timeStepsOnFile[0].month, timeStepsOnFile[0].day ); std::vector dateTimes; for ( const auto& timeStep : timeStepsOnFile ) { auto dateTime = dateTimeFromTimeStepOnFile( timeStep, startDate, startDayOffset ); dateTimes.push_back( dateTime ); } return dateTimes; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RifReaderOpmCommon::verifyActiveCellInfo( int activeSizeMat, int activeSizeFrac ) { if ( m_initFile == nullptr ) return true; int activeCells = 0; for ( const auto& name : m_gridNames ) { activeCells += m_initFile->activeCells( name ); } return activeCells == ( activeSizeFrac + activeSizeMat ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector> RifReaderOpmCommon::readActiveCellInfoFromPorv( RigEclipseCaseData* eclipseCaseData, bool isDualPorosity ) { if ( m_initFile == nullptr ) return {}; std::vector> activeCellsAllGrids; bool divideCellCountByTwo = isDualPorosity; const int nGrids = (int)m_gridNames.size(); for ( int gridIdx = 0; gridIdx < nGrids; gridIdx++ ) { auto porvValues = m_initFile->getInitData( "PORV", m_gridNames[gridIdx] ); int activeCellCount = (int)porvValues.size(); if ( divideCellCountByTwo ) { activeCellCount /= 2; } std::vector activeCellsOneGrid; activeCellsOneGrid.resize( activeCellCount, 0 ); for ( int poreValueIndex = 0; poreValueIndex < static_cast( porvValues.size() ); poreValueIndex++ ) { int indexToCell = poreValueIndex; if ( indexToCell >= activeCellCount ) { indexToCell = poreValueIndex - activeCellCount; } if ( porvValues[poreValueIndex] > 0.0f ) { if ( isDualPorosity ) { if ( poreValueIndex < activeCellCount ) { activeCellsOneGrid[indexToCell] += (int)ActiveType::ACTIVE_MATRIX_VALUE; } else { activeCellsOneGrid[indexToCell] += (int)ActiveType::ACTIVE_FRACTURE_VALUE; } } else { activeCellsOneGrid[indexToCell] += (int)ActiveType::ACTIVE_MATRIX_VALUE; } } } activeCellsAllGrids.push_back( activeCellsOneGrid ); } return activeCellsAllGrids; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifReaderOpmCommon::updateActiveCellInfo( RigEclipseCaseData* eclipseCaseData, Opm::EclIO::EGrid& opmGrid, std::vector& lgrGrids, RigMainGrid* mainGrid ) { auto activeCellInfoPerGrid = readActiveCellInfoFromPorv( eclipseCaseData, opmGrid.porosity_mode() > 0 ); int nInfos = (int)activeCellInfoPerGrid.size(); if ( nInfos > 0 ) { int gridIdx = 0; opmGrid.set_active_cells( activeCellInfoPerGrid[gridIdx++] ); for ( auto& lgr : lgrGrids ) { if ( gridIdx < nInfos ) lgr.set_active_cells( activeCellInfoPerGrid[gridIdx++] ); } } }