///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2021 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 "RimStreamlineDataAccess.h" #include "RigCaseCellResultsData.h" #include "RigCell.h" #include "RigEclipseCaseData.h" #include "RigEclipseResultAddress.h" #include "RigMainGrid.h" #include "RigResultAccessor.h" #include "RigResultAccessorFactory.h" #include "cafAssert.h" //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimStreamlineDataAccess::RimStreamlineDataAccess() : m_data( nullptr ) , m_grid( nullptr ) { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimStreamlineDataAccess::~RimStreamlineDataAccess() { } //-------------------------------------------------------------------------------------------------- /// Set up data accessors to access the selected flroil/gas/wat data for all faces //-------------------------------------------------------------------------------------------------- bool RimStreamlineDataAccess::setupDataAccess( RigMainGrid* grid, RigEclipseCaseData* data, std::list phases, int timeIdx ) { m_grid = grid; m_data = data; m_dataAccess.clear(); for ( auto phase : phases ) { m_dataAccess[phase] = std::vector>(); // Note: NEG_? accessors are set to POS_? accessors, but will be referring the neighbor cell when used m_dataAccess[phase].push_back( getDataAccessor( cvf::StructGridInterface::FaceType::POS_I, phase, timeIdx ) ); m_dataAccess[phase].push_back( getDataAccessor( cvf::StructGridInterface::FaceType::POS_I, phase, timeIdx ) ); m_dataAccess[phase].push_back( getDataAccessor( cvf::StructGridInterface::FaceType::POS_J, phase, timeIdx ) ); m_dataAccess[phase].push_back( getDataAccessor( cvf::StructGridInterface::FaceType::POS_J, phase, timeIdx ) ); m_dataAccess[phase].push_back( getDataAccessor( cvf::StructGridInterface::FaceType::POS_K, phase, timeIdx ) ); m_dataAccess[phase].push_back( getDataAccessor( cvf::StructGridInterface::FaceType::POS_K, phase, timeIdx ) ); } for ( auto& pair : m_dataAccess ) { for ( auto& access : pair.second ) { if ( access.isNull() ) return false; } } return true; } //-------------------------------------------------------------------------------------------------- /// Return a data accessor for the given phase and face and time step //-------------------------------------------------------------------------------------------------- cvf::ref RimStreamlineDataAccess::getDataAccessor( cvf::StructGridInterface::FaceType faceIdx, RiaDefines::PhaseType phase, int timeIdx ) { RiaDefines::PorosityModelType porModel = RiaDefines::PorosityModelType::MATRIX_MODEL; RigCaseCellResultsData* data = m_data->results( porModel ); QString resultname = gridResultNameFromPhase( phase, faceIdx ); int gridIdx = 0; RigEclipseResultAddress address( RiaDefines::ResultCatType::DYNAMIC_NATIVE, resultname ); // Make sure we have the data loaded. // NB, will trigger load of data for all time steps data->ensureKnownResultLoaded( address ); return RigResultAccessorFactory::createFromResultAddress( m_data, gridIdx, porModel, timeIdx, address ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimStreamlineDataAccess::gridResultNameFromPhase( RiaDefines::PhaseType phase, cvf::StructGridInterface::FaceType faceIdx ) const { QString retval = ""; switch ( phase ) { case RiaDefines::PhaseType::GAS_PHASE: retval += "FLRGAS"; break; case RiaDefines::PhaseType::OIL_PHASE: retval += "FLROIL"; break; case RiaDefines::PhaseType::WATER_PHASE: retval += "FLRWAT"; break; default: CAF_ASSERT( false ); break; } switch ( faceIdx ) { case cvf::StructGridInterface::FaceType::POS_I: retval += "I+"; break; case cvf::StructGridInterface::FaceType::POS_J: retval += "J+"; break; case cvf::StructGridInterface::FaceType::POS_K: retval += "K+"; break; default: CAF_ASSERT( false ); break; } return retval; } //-------------------------------------------------------------------------------------------------- /// Return the face scalar value for the given cell and NEG_? face, by using the neighbor cell //-------------------------------------------------------------------------------------------------- double RimStreamlineDataAccess::negFaceRate( RigCell cell, cvf::StructGridInterface::FaceType faceIdx, RiaDefines::PhaseType phase ) const { double retval = 0.0; // NEG_? face values must be read from the neighbor cells RigCell neighborCell = cell.neighborCell( faceIdx ); if ( neighborCell.isInvalid() ) return retval; std::vector> access = m_dataAccess.at( phase ); retval = access[faceIdx]->cellScalar( neighborCell.mainGridCellIndex() ); double area = cell.faceNormalWithAreaLength( faceIdx ).length(); if ( area > 1.0e-4 ) retval /= area; else retval = 0.0; if ( std::isinf( retval ) ) retval = 0.0; // change sign to get proper rate value direction (out of one cell is into the next) return -1.0 * retval; } //-------------------------------------------------------------------------------------------------- /// Return the face scalar value for the given cell and POS_? face //-------------------------------------------------------------------------------------------------- double RimStreamlineDataAccess::posFaceRate( RigCell cell, cvf::StructGridInterface::FaceType faceIdx, RiaDefines::PhaseType phase ) const { std::vector> access = m_dataAccess.at( phase ); double retval = access[faceIdx]->cellScalar( cell.mainGridCellIndex() ); double length = cell.faceNormalWithAreaLength( faceIdx ).length(); if ( length != 0.0 ) retval /= length; else retval = 0.0; if ( std::isinf( retval ) ) retval = 0.0; return retval; } //-------------------------------------------------------------------------------------------------- /// Return the face scalar value for the given cell and face /// Positive values is flow out of the cell, negative values is flow into the cell //-------------------------------------------------------------------------------------------------- double RimStreamlineDataAccess::faceRate( RigCell cell, cvf::StructGridInterface::FaceType faceIdx, RiaDefines::PhaseType phase ) const { if ( faceIdx % 2 == 0 ) return posFaceRate( cell, faceIdx, phase ); return negFaceRate( cell, faceIdx, phase ); } //-------------------------------------------------------------------------------------------------- /// Return the face scalar value for the given cell and face, by combining flow for all specified phases /// Positive values is flow out of the cell, negative values is flow into the cell //-------------------------------------------------------------------------------------------------- double RimStreamlineDataAccess::combinedFaceRate( RigCell cell, cvf::StructGridInterface::FaceType faceIdx, std::list phases, double direction, RiaDefines::PhaseType& outDominantPhase ) const { double retValue = 0.0; outDominantPhase = phases.front(); double max = 0.0; for ( auto phase : phases ) { double tmp = 0.0; if ( faceIdx % 2 == 0 ) tmp = posFaceRate( cell, faceIdx, phase ); else tmp = negFaceRate( cell, faceIdx, phase ); if ( tmp * direction > max ) { outDominantPhase = phase; max = std::abs( tmp ); } retValue += tmp; } return retValue; }