///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2011- Statoil ASA // Copyright (C) 2013- Ceetron Solutions AS // Copyright (C) 2011-2012 Ceetron AS // // 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 "RigEclipseCaseData.h" #include "RifReaderEclipseOutput.h" #include "RigActiveCellInfo.h" #include "RigCaseCellResultsData.h" #include "RigEclipseResultAddress.h" #include "RigEquil.h" #include "RigFormationNames.h" #include "RigMainGrid.h" #include "RigResultAccessorFactory.h" #include "RigSimWellData.h" #include "RigSimulationWellCenterLineCalculator.h" #include "RigSimulationWellCoordsAndMD.h" #include "RigVirtualPerforationTransmissibilities.h" #include "RigWellPath.h" #include "RigWellResultFrame.h" #include "RigWellResultPoint.h" #include //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigEclipseCaseData::RigEclipseCaseData( RimEclipseCase* ownerCase ) : m_hasParsedDeckForEquilData( false ) { m_mainGrid = new RigMainGrid(); m_ownerCase = ownerCase; m_matrixModelResults = new RigCaseCellResultsData( this, RiaDefines::PorosityModelType::MATRIX_MODEL ); m_fractureModelResults = new RigCaseCellResultsData( this, RiaDefines::PorosityModelType::FRACTURE_MODEL ); m_activeCellInfo = new RigActiveCellInfo; m_fractureActiveCellInfo = new RigActiveCellInfo; m_matrixModelResults->setActiveCellInfo( m_activeCellInfo.p() ); m_fractureModelResults->setActiveCellInfo( m_fractureActiveCellInfo.p() ); m_unitsType = RiaDefines::EclipseUnitSystem::UNITS_METRIC; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigEclipseCaseData::~RigEclipseCaseData() { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigMainGrid* RigEclipseCaseData::mainGrid() { return m_mainGrid.p(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RigMainGrid* RigEclipseCaseData::mainGrid() const { return m_mainGrid.p(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigEclipseCaseData::setMainGrid( RigMainGrid* mainGrid ) { m_mainGrid = mainGrid; m_matrixModelResults->setMainGrid( m_mainGrid.p() ); m_fractureModelResults->setMainGrid( m_mainGrid.p() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigEclipseCaseData::allGrids( std::vector* grids ) { CVF_ASSERT( grids ); if ( m_mainGrid.isNull() ) { return; } size_t i; for ( i = 0; i < m_mainGrid->gridCount(); i++ ) { grids->push_back( m_mainGrid->gridByIndex( i ) ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigEclipseCaseData::allGrids( std::vector* grids ) const { CVF_ASSERT( grids ); if ( m_mainGrid.isNull() ) { return; } size_t i; for ( i = 0; i < m_mainGrid->gridCount(); i++ ) { grids->push_back( m_mainGrid->gridByIndex( i ) ); } } //-------------------------------------------------------------------------------------------------- /// Get grid by index. The main grid has index 0, so the first lgr has index 1 //-------------------------------------------------------------------------------------------------- const RigGridBase* RigEclipseCaseData::grid( size_t index ) const { CVF_ASSERT( m_mainGrid.notNull() ); return m_mainGrid->gridByIndex( index ); } //-------------------------------------------------------------------------------------------------- /// Get grid by index. The main grid has index 0, so the first lgr has index 1 //-------------------------------------------------------------------------------------------------- RigGridBase* RigEclipseCaseData::grid( size_t index ) { CVF_ASSERT( m_mainGrid.notNull() ); return m_mainGrid->gridByIndex( index ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RigGridBase* RigEclipseCaseData::grid( const QString& gridName ) const { if ( m_mainGrid.isNull() ) { return nullptr; } if ( gridName.isEmpty() ) { return m_mainGrid.p(); } size_t i; for ( i = 0; i < m_mainGrid->gridCount(); i++ ) { const RigGridBase* grid = m_mainGrid->gridByIndex( i ); if ( QString::fromStdString( grid->gridName() ) == gridName ) return grid; } return nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t RigEclipseCaseData::gridCount() const { CVF_ASSERT( m_mainGrid.notNull() ); return m_mainGrid->gridCount(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigEclipseCaseData::computeWellCellsPrGrid() { // If we have computed this already, return if ( m_wellCellsInGrid.size() ) return; std::vector grids; this->allGrids( &grids ); // Debug code used to display grid names and grid sizes /* size_t totCellCount = 0; for (auto g : grids) { qDebug() << g->gridName().data(); qDebug() << g->cellCountI() << " " << g->cellCountJ() << " " << g->cellCountK() << " "; size_t cellCount = g->cellCount(); totCellCount += cellCount; qDebug() << cellCount; qDebug() << "\n"; } qDebug() << "\nTotal cell count " << totCellCount; */ size_t gIdx; // Allocate and initialize the arrays m_wellCellsInGrid.resize( grids.size() ); m_gridCellToResultWellIndex.resize( grids.size() ); for ( gIdx = 0; gIdx < grids.size(); ++gIdx ) { if ( m_wellCellsInGrid[gIdx].isNull() || m_wellCellsInGrid[gIdx]->size() != grids[gIdx]->cellCount() ) { m_wellCellsInGrid[gIdx] = new cvf::UByteArray; m_wellCellsInGrid[gIdx]->resize( grids[gIdx]->cellCount() ); m_gridCellToResultWellIndex[gIdx] = new cvf::UIntArray; m_gridCellToResultWellIndex[gIdx]->resize( grids[gIdx]->cellCount() ); } m_wellCellsInGrid[gIdx]->setAll( false ); m_gridCellToResultWellIndex[gIdx]->setAll( cvf::UNDEFINED_UINT ); } // Fill arrays with data size_t wIdx; for ( wIdx = 0; wIdx < m_simWellData.size(); ++wIdx ) { size_t tIdx; for ( tIdx = 0; tIdx < m_simWellData[wIdx]->m_wellCellsTimeSteps.size(); ++tIdx ) { RigWellResultFrame& wellCells = m_simWellData[wIdx]->m_wellCellsTimeSteps[tIdx]; // Well result branches for ( const auto& wellSegment : wellCells.wellResultBranches() ) { for ( const auto& resultPoint : wellSegment.branchResultPoints() ) { size_t gridIndex = resultPoint.gridIndex(); size_t gridCellIndex = resultPoint.cellIndex(); if ( gridIndex < m_wellCellsInGrid.size() && gridCellIndex < m_wellCellsInGrid[gridIndex]->size() ) { // NOTE : We do not check if the grid cell is active as we do for well head. // If we add test for active cell, thorough testing and verification of the new behavior must // be addressed m_wellCellsInGrid[gridIndex]->set( gridCellIndex, true ); m_gridCellToResultWellIndex[gridIndex]->set( gridCellIndex, static_cast( wIdx ) ); } } } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigEclipseCaseData::setSimWellData( const cvf::Collection& data ) { m_simWellData = data; m_wellCellsInGrid.clear(); m_gridCellToResultWellIndex.clear(); computeWellCellsPrGrid(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RigEclipseCaseData::findSortedWellNames() const { std::set sortedWellNames; const cvf::Collection& simWellData = wellResults(); for ( size_t wIdx = 0; wIdx < simWellData.size(); ++wIdx ) { sortedWellNames.insert( simWellData[wIdx]->m_wellName ); } return sortedWellNames; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RigSimWellData* RigEclipseCaseData::findSimWellData( QString wellName ) const { for ( size_t wIdx = 0; wIdx < m_simWellData.size(); ++wIdx ) { if ( m_simWellData[wIdx]->m_wellName == wellName ) { return m_simWellData[wIdx].p(); } } return nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const cvf::UByteArray* RigEclipseCaseData::wellCellsInGrid( size_t gridIndex ) { computeWellCellsPrGrid(); CVF_ASSERT( gridIndex < m_wellCellsInGrid.size() ); return m_wellCellsInGrid[gridIndex].p(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const cvf::UIntArray* RigEclipseCaseData::gridCellToResultWellIndex( size_t gridIndex ) { computeWellCellsPrGrid(); CVF_ASSERT( gridIndex < m_gridCellToResultWellIndex.size() ); return m_gridCellToResultWellIndex[gridIndex].p(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RigCell& RigEclipseCaseData::cellFromWellResultCell( const RigWellResultPoint& wellResultCell ) const { CVF_ASSERT( wellResultCell.isCell() ); size_t gridIndex = wellResultCell.gridIndex(); size_t gridCellIndex = wellResultCell.cellIndex(); std::vector grids; allGrids( &grids ); return grids[gridIndex]->cell( gridCellIndex ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RigEclipseCaseData::findSharedSourceFace( cvf::StructGridInterface::FaceType& sharedSourceFace, const RigWellResultPoint& sourceWellCellResult, const RigWellResultPoint& otherWellCellResult ) const { size_t gridIndex = sourceWellCellResult.gridIndex(); size_t gridCellIndex = sourceWellCellResult.cellIndex(); size_t otherGridIndex = otherWellCellResult.gridIndex(); size_t otherGridCellIndex = otherWellCellResult.cellIndex(); if ( gridIndex != otherGridIndex ) return false; std::vector grids; allGrids( &grids ); const RigGridBase* grid = grids[gridIndex]; size_t i, j, k; grid->ijkFromCellIndex( gridCellIndex, &i, &j, &k ); size_t faceIdx; for ( faceIdx = 0; faceIdx < 6; faceIdx++ ) { cvf::StructGridInterface::FaceType sourceFace = static_cast( faceIdx ); size_t ni, nj, nk; RigGridBase::neighborIJKAtCellFace( i, j, k, sourceFace, &ni, &nj, &nk ); if ( grid->isCellValid( ni, nj, nk ) ) { size_t neighborCellIndex = grid->cellIndexFromIJK( ni, nj, nk ); if ( neighborCellIndex == otherGridCellIndex ) { sharedSourceFace = sourceFace; return true; } } } return false; } //-------------------------------------------------------------------------------------------------- /// Helper class used to find min/max range for valid and active cells //-------------------------------------------------------------------------------------------------- class CellRangeBB { public: CellRangeBB() : m_min( cvf::UNDEFINED_SIZE_T, cvf::UNDEFINED_SIZE_T, cvf::UNDEFINED_SIZE_T ) , m_max( cvf::Vec3st::ZERO ) { } void add( size_t i, size_t j, size_t k ) { if ( i < m_min.x() ) m_min.x() = i; if ( j < m_min.y() ) m_min.y() = j; if ( k < m_min.z() ) m_min.z() = k; if ( i > m_max.x() ) m_max.x() = i; if ( j > m_max.y() ) m_max.y() = j; if ( k > m_max.z() ) m_max.z() = k; } public: cvf::Vec3st m_min; cvf::Vec3st m_max; }; //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigEclipseCaseData::computeActiveCellIJKBBox() { if ( m_mainGrid.notNull() && m_activeCellInfo.notNull() && m_fractureActiveCellInfo.notNull() ) { CellRangeBB matrixModelActiveBB; CellRangeBB fractureModelActiveBB; size_t idx; for ( idx = 0; idx < m_mainGrid->cellCount(); idx++ ) { size_t i, j, k; m_mainGrid->ijkFromCellIndex( idx, &i, &j, &k ); if ( m_activeCellInfo->isActive( idx ) ) { matrixModelActiveBB.add( i, j, k ); } if ( m_fractureActiveCellInfo->isActive( idx ) ) { fractureModelActiveBB.add( i, j, k ); } } m_activeCellInfo->setIJKBoundingBox( matrixModelActiveBB.m_min, matrixModelActiveBB.m_max ); m_fractureActiveCellInfo->setIJKBoundingBox( fractureModelActiveBB.m_min, fractureModelActiveBB.m_max ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigEclipseCaseData::computeActiveCellBoundingBoxes() { computeActiveCellIJKBBox(); computeActiveCellsGeometryBoundingBox(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RigEclipseCaseData::simulationWellNames() const { std::vector wellNames; for ( const auto& wellResult : wellResults() ) { wellNames.push_back( wellResult->m_wellName ); } return wellNames; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RigEclipseCaseData::hasSimulationWell( const QString& simWellName ) const { const auto wellNames = simulationWellNames(); return std::find( wellNames.begin(), wellNames.end(), simWellName ) != wellNames.end(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RigEclipseCaseData::simulationWellBranches( const QString& simWellName, bool includeAllCellCenters, bool useAutoDetectionOfBranches ) const { std::vector branches; if ( simWellName.isEmpty() || simWellName.toUpper() == "NONE" ) { return branches; } const RigSimWellData* simWellData = findSimWellData( simWellName ); if ( !simWellData ) return branches; std::tuple simWellSeachItem = std::make_tuple( simWellName, includeAllCellCenters, useAutoDetectionOfBranches ); if ( m_simWellBranchCache.find( simWellSeachItem ) == m_simWellBranchCache.end() ) { const auto simWellBranches = RigSimulationWellCenterLineCalculator::calculateWellPipeCenterlineForTimeStep( this, simWellData, -1, useAutoDetectionOfBranches, includeAllCellCenters ); m_simWellBranchCache.insert( std::make_pair( simWellSeachItem, cvf::Collection() ) ); for ( const auto& [coords, wellCells] : simWellBranches ) { auto wellMdCalculator = RigSimulationWellCoordsAndMD( coords ); cvf::ref newWellPath = new RigWellPath( wellMdCalculator.wellPathPoints(), wellMdCalculator.measuredDepths() ); m_simWellBranchCache[simWellSeachItem].push_back( newWellPath.p() ); } } for ( const auto& branch : m_simWellBranchCache[simWellSeachItem] ) { branches.push_back( branch.p() ); } return branches; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigEclipseCaseData::setVirtualPerforationTransmissibilities( RigVirtualPerforationTransmissibilities* virtualPerforationTransmissibilities ) { m_virtualPerforationTransmissibilities = virtualPerforationTransmissibilities; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RigVirtualPerforationTransmissibilities* RigEclipseCaseData::virtualPerforationTransmissibilities() const { return m_virtualPerforationTransmissibilities.p(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigEclipseCaseData::ensureDeckIsParsedForEquilData( const QString& dataDeckFile, const QString& includeFileAbsolutePathPrefix ) { if ( !m_hasParsedDeckForEquilData ) { RifReaderEclipseOutput::importEquilData( dataDeckFile, includeFileAbsolutePathPrefix, this ); m_hasParsedDeckForEquilData = true; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RigEclipseCaseData::equilData() const { return m_equil; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigEclipseCaseData::setEquilData( const std::vector& equilObjects ) { m_equil = equilObjects; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigActiveCellInfo* RigEclipseCaseData::activeCellInfo( RiaDefines::PorosityModelType porosityModel ) { if ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL ) { return m_activeCellInfo.p(); } return m_fractureActiveCellInfo.p(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RigActiveCellInfo* RigEclipseCaseData::activeCellInfo( RiaDefines::PorosityModelType porosityModel ) const { if ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL ) { return m_activeCellInfo.p(); } return m_fractureActiveCellInfo.p(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigEclipseCaseData::setActiveCellInfo( RiaDefines::PorosityModelType porosityModel, RigActiveCellInfo* activeCellInfo ) { if ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL ) { m_activeCellInfo = activeCellInfo; m_matrixModelResults->setActiveCellInfo( m_activeCellInfo.p() ); } else { m_fractureActiveCellInfo = activeCellInfo; m_fractureModelResults->setActiveCellInfo( m_fractureActiveCellInfo.p() ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RigEclipseCaseData::hasFractureResults() const { if ( activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL ) && activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL )->reservoirActiveCellCount() > 0 ) { return true; } return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigEclipseCaseData::computeActiveCellsGeometryBoundingBox() { if ( m_activeCellInfo.isNull() || m_fractureActiveCellInfo.isNull() ) { return; } if ( m_mainGrid.isNull() ) { cvf::BoundingBox bb; m_activeCellInfo->setGeometryBoundingBox( bb ); m_fractureActiveCellInfo->setGeometryBoundingBox( bb ); return; } RigActiveCellInfo* activeInfos[2]; activeInfos[0] = m_fractureActiveCellInfo.p(); activeInfos[1] = m_activeCellInfo.p(); // Last, to make this bb.min become display offset cvf::BoundingBox bb; for ( int acIdx = 0; acIdx < 2; ++acIdx ) { bb.reset(); if ( m_mainGrid->nodes().size() == 0 ) { bb.add( cvf::Vec3d::ZERO ); } else { std::array hexCorners; for ( size_t i = 0; i < m_mainGrid->cellCount(); i++ ) { if ( activeInfos[acIdx]->isActive( i ) ) { m_mainGrid->cellCornerVertices( i, hexCorners.data() ); for ( const auto& corner : hexCorners ) { bb.add( corner ); } } } } activeInfos[acIdx]->setGeometryBoundingBox( bb ); } m_mainGrid->setDisplayModelOffset( bb.min() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigEclipseCaseData::setActiveFormationNames( RigFormationNames* activeFormationNames ) { m_matrixModelResults->setActiveFormationNames( activeFormationNames ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RigFormationNames* RigEclipseCaseData::activeFormationNames() const { return m_matrixModelResults->activeFormationNames(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector RigEclipseCaseData::formationNames() const { if ( activeFormationNames() ) { return activeFormationNames()->formationNames(); } return {}; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigAllanDiagramData* RigEclipseCaseData::allanDiagramData() { return m_matrixModelResults->allanDiagramData(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigCaseCellResultsData* RigEclipseCaseData::results( RiaDefines::PorosityModelType porosityModel ) { if ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL ) { return m_matrixModelResults.p(); } return m_fractureModelResults.p(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RigCaseCellResultsData* RigEclipseCaseData::results( RiaDefines::PorosityModelType porosityModel ) const { if ( porosityModel == RiaDefines::PorosityModelType::MATRIX_MODEL ) { return m_matrixModelResults.p(); } return m_fractureModelResults.p(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector* RigEclipseCaseData::resultValues( RiaDefines::PorosityModelType porosityModel, RiaDefines::ResultCatType type, const QString& resultName, size_t timeStepIndex ) { RigCaseCellResultsData* gridCellResults = this->results( porosityModel ); const std::vector* swatResults = nullptr; if ( gridCellResults->ensureKnownResultLoaded( RigEclipseResultAddress( type, resultName ) ) ) { swatResults = &( gridCellResults->cellScalarResults( RigEclipseResultAddress( type, resultName ), timeStepIndex ) ); } return swatResults; }