Files
ResInsight/ApplicationLibCode/ReservoirDataModel/RigNNCData.cpp
Magne Sjaastad 529c9af9a4 Guard recursive updates if NNC data import is disabled
Import of NNC data can be turned off in Preferences. If this is off, loading of projects using NNC data can enter a recursive update loop.
2022-12-16 15:29:50 +01:00

719 lines
27 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) Statoil ASA
// Copyright (C) Ceetron Solutions 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 <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RigNNCData.h"
#include "RigCellFaceGeometryTools.h"
#include "RigEclipseResultAddress.h"
#include "RigMainGrid.h"
#include "RiaLogging.h"
#include "cafProgressInfo.h"
#include "cvfGeometryTools.h"
#include <QString>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigNNCData::RigNNCData()
: m_eclipseConnectionCount( 0 )
, m_havePolygonsForEclipseConnections( false )
, m_haveGeneratedConnections( false )
, m_mainGrid( nullptr )
, m_activeCellInfo( nullptr )
, m_computeNncForInactiveCells( false )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigNNCData::setSourceDataForProcessing( RigMainGrid* mainGrid,
const RigActiveCellInfo* activeCellInfo,
bool includeInactiveCells )
{
m_mainGrid = mainGrid;
m_activeCellInfo = activeCellInfo;
m_computeNncForInactiveCells = includeInactiveCells;
m_havePolygonsForEclipseConnections = false;
m_haveGeneratedConnections = false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigNNCData::buildPolygonsForEclipseConnections()
{
if ( m_havePolygonsForEclipseConnections ) return;
if ( !m_mainGrid ) return;
#pragma omp parallel for
for ( int cnIdx = 0; cnIdx < static_cast<int>( eclipseConnectionCount() ); ++cnIdx )
{
const RigCell& c1 = m_mainGrid->globalCellArray()[m_connections[cnIdx].c1GlobIdx()];
const RigCell& c2 = m_mainGrid->globalCellArray()[m_connections[cnIdx].c2GlobIdx()];
std::vector<size_t> connectionPolygon;
std::vector<cvf::Vec3d> connectionIntersections;
cvf::StructGridInterface::FaceType connectionFace = cvf::StructGridInterface::NO_FACE;
connectionFace = RigCellFaceGeometryTools::calculateCellFaceOverlap( c1,
c2,
*m_mainGrid,
&connectionPolygon,
&connectionIntersections );
if ( connectionFace != cvf::StructGridInterface::NO_FACE )
{
m_connections[cnIdx].setFace( connectionFace );
m_connections[cnIdx].setPolygon( RigCellFaceGeometryTools::extractPolygon( m_mainGrid->nodes(),
connectionPolygon,
connectionIntersections ) );
}
}
m_havePolygonsForEclipseConnections = true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigNNCData::computeAdditionalNncs( const RigMainGrid* mainGrid,
const RigActiveCellInfo* activeCellInfo,
bool includeInactiveCells )
{
RigConnectionContainer otherConnections =
RigCellFaceGeometryTools::computeOtherNncs( mainGrid, m_connections, activeCellInfo, includeInactiveCells );
if ( !otherConnections.empty() )
{
m_connections.push_back( otherConnections );
// Transmissibility values from Eclipse has been read into propertyNameCombTrans in
// RifReaderEclipseOutput::transferStaticNNCData(). Initialize computed NNCs with zero transmissibility
auto it = m_connectionResults.find( RiaDefines::propertyNameCombTrans() );
if ( it != m_connectionResults.end() )
{
if ( !it->second.empty() )
{
it->second[0].resize( m_connections.size(), 0.0 );
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RigNNCData::connectionsWithNoCommonArea( QStringList& connectionTextFirstItems, size_t maxItemCount )
{
size_t connectionWithNoCommonAreaCount = 0;
for ( size_t connIndex = 0; connIndex < m_connections.size(); connIndex++ )
{
if ( !m_connections[connIndex].hasCommonArea() )
{
connectionWithNoCommonAreaCount++;
if ( connectionTextFirstItems.size() < static_cast<int>( maxItemCount ) )
{
QString firstConnectionText;
QString secondConnectionText;
{
size_t gridLocalCellIndex;
const RigGridBase* hostGrid =
m_mainGrid->gridAndGridLocalIdxFromGlobalCellIdx( m_connections[connIndex].c1GlobIdx(),
&gridLocalCellIndex );
size_t i, j, k;
if ( hostGrid->ijkFromCellIndex( gridLocalCellIndex, &i, &j, &k ) )
{
// Adjust to 1-based Eclipse indexing
i++;
j++;
k++;
if ( !hostGrid->isMainGrid() )
{
QString gridName = QString::fromStdString( hostGrid->gridName() );
firstConnectionText = gridName + " ";
}
firstConnectionText += QString( "[%1 %2 %3] - " ).arg( i ).arg( j ).arg( k );
}
}
{
size_t gridLocalCellIndex;
const RigGridBase* hostGrid =
m_mainGrid->gridAndGridLocalIdxFromGlobalCellIdx( m_connections[connIndex].c2GlobIdx(),
&gridLocalCellIndex );
size_t i, j, k;
if ( hostGrid->ijkFromCellIndex( gridLocalCellIndex, &i, &j, &k ) )
{
// Adjust to 1-based Eclipse indexing
i++;
j++;
k++;
if ( !hostGrid->isMainGrid() )
{
QString gridName = QString::fromStdString( hostGrid->gridName() );
secondConnectionText = gridName + " ";
}
secondConnectionText += QString( "[%1 %2 %3]" ).arg( i ).arg( j ).arg( k );
}
}
connectionTextFirstItems.push_back( firstConnectionText + secondConnectionText );
}
}
}
return connectionWithNoCommonAreaCount;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RigNNCData::ensureAllConnectionDataIsProcessed()
{
if ( m_haveGeneratedConnections ) return false;
// Return false if we have no data to process to avoid recursive updates in consuming code
if ( !m_mainGrid ) return false;
if ( m_mainGrid )
{
caf::ProgressInfo progressInfo( 3, "Computing NNC Data" );
RiaLogging::info( "NNC geometry computation - starting process" );
buildPolygonsForEclipseConnections();
progressInfo.incrementProgress();
computeAdditionalNncs( m_mainGrid, m_activeCellInfo, m_computeNncForInactiveCells );
progressInfo.incrementProgress();
m_haveGeneratedConnections = true;
m_mainGrid->distributeNNCsToFaults();
QStringList noCommonAreaText;
const size_t maxItemCount = 20;
size_t noCommonAreaCount = connectionsWithNoCommonArea( noCommonAreaText, maxItemCount );
RiaLogging::info( "NNC geometry computation - completed process" );
RiaLogging::info( QString( "Native NNC count : %1" ).arg( eclipseConnectionCount() ) );
RiaLogging::info( QString( "Computed NNC count : %1" ).arg( m_connections.size() ) );
RiaLogging::info( QString( "NNCs with no common area count : %1" ).arg( noCommonAreaCount ) );
if ( !noCommonAreaText.isEmpty() )
{
RiaLogging::info( QString( "Listing first %1 NNCs with no common area " ).arg( noCommonAreaText.size() ) );
for ( const auto& s : noCommonAreaText )
{
RiaLogging::info( s );
}
}
}
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigNNCData::setEclipseConnections( RigConnectionContainer& eclipseConnections )
{
m_connections = eclipseConnections;
m_eclipseConnectionCount = m_connections.size();
m_haveGeneratedConnections = false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RigNNCData::eclipseConnectionCount() const
{
return m_eclipseConnectionCount;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const RigConnectionContainer& RigNNCData::availableConnections() const
{
// Return connections without calling ensureConnectionDataIsProcessed() to avoid potential heavy computations
// Relevant if only native connection data is required
// NB: If computeAdditionalNncs() is called before this method, the size of this collection is larger than
// nativeConnectionCount()
return m_connections;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigConnectionContainer& RigNNCData::allConnections()
{
ensureAllConnectionDataIsProcessed();
return m_connections;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double>& RigNNCData::makeStaticConnectionScalarResult( QString nncDataType )
{
ensureAllConnectionDataIsProcessed();
std::vector<std::vector<double>>& results = m_connectionResults[nncDataType];
results.resize( 1 );
results[0].resize( m_connections.size(), HUGE_VAL );
return results[0];
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigNNCData::makeScalarResultAndSetValues( const QString& nncDataType, const std::vector<double>& values )
{
std::vector<std::vector<double>>& results = m_connectionResults[nncDataType];
results.resize( 1 );
results[0] = values;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<double>* RigNNCData::staticConnectionScalarResult( const RigEclipseResultAddress& resVarAddr ) const
{
QString nncDataType = getNNCDataTypeFromScalarResultIndex( resVarAddr );
if ( nncDataType.isNull() ) return nullptr;
std::map<QString, std::vector<std::vector<double>>>::const_iterator it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
CVF_ASSERT( it->second.size() == 1 );
return &( it->second[0] );
}
else
{
return nullptr;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<double>* RigNNCData::staticConnectionScalarResultByName( const QString& nncDataType ) const
{
std::map<QString, std::vector<std::vector<double>>>::const_iterator it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
CVF_ASSERT( it->second.size() == 1 );
return &( it->second[0] );
}
else
{
return nullptr;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<std::vector<double>>& RigNNCData::makeDynamicConnectionScalarResult( QString nncDataType, size_t timeStepCount )
{
auto& results = m_connectionResults[nncDataType];
results.resize( timeStepCount );
return results;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<std::vector<double>>*
RigNNCData::dynamicConnectionScalarResult( const RigEclipseResultAddress& resVarAddr ) const
{
QString nncDataType = getNNCDataTypeFromScalarResultIndex( resVarAddr );
if ( nncDataType.isNull() ) return nullptr;
auto it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
return &( it->second );
}
else
{
return nullptr;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<double>* RigNNCData::dynamicConnectionScalarResult( const RigEclipseResultAddress& resVarAddr,
size_t timeStep ) const
{
QString nncDataType = getNNCDataTypeFromScalarResultIndex( resVarAddr );
if ( nncDataType.isNull() ) return nullptr;
auto it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
if ( it->second.size() > timeStep )
{
return &( it->second[timeStep] );
}
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<std::vector<double>>* RigNNCData::dynamicConnectionScalarResultByName( const QString& nncDataType ) const
{
auto it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
return &( it->second );
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<double>* RigNNCData::dynamicConnectionScalarResultByName( const QString& nncDataType, size_t timeStep ) const
{
auto it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
if ( it->second.size() > timeStep )
{
return &( it->second[timeStep] );
}
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<std::vector<double>>& RigNNCData::makeGeneratedConnectionScalarResult( QString nncDataType, size_t timeStepCount )
{
auto& results = m_connectionResults[nncDataType];
results.resize( timeStepCount );
return results;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<std::vector<double>>*
RigNNCData::generatedConnectionScalarResult( const RigEclipseResultAddress& resVarAddr ) const
{
QString nncDataType = getNNCDataTypeFromScalarResultIndex( resVarAddr );
if ( nncDataType.isNull() ) return nullptr;
auto it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
return &( it->second );
}
else
{
return nullptr;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<double>* RigNNCData::generatedConnectionScalarResult( const RigEclipseResultAddress& resVarAddr,
size_t timeStep ) const
{
QString nncDataType = getNNCDataTypeFromScalarResultIndex( resVarAddr );
if ( nncDataType.isNull() ) return nullptr;
auto it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
if ( it->second.size() > timeStep )
{
return &( it->second[timeStep] );
}
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<std::vector<double>>* RigNNCData::generatedConnectionScalarResult( const RigEclipseResultAddress& resVarAddr )
{
QString nncDataType = getNNCDataTypeFromScalarResultIndex( resVarAddr );
if ( nncDataType.isNull() ) return nullptr;
auto it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
return &( it->second );
}
else
{
return nullptr;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double>* RigNNCData::generatedConnectionScalarResult( const RigEclipseResultAddress& resVarAddr, size_t timeStep )
{
QString nncDataType = getNNCDataTypeFromScalarResultIndex( resVarAddr );
if ( nncDataType.isNull() ) return nullptr;
auto it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
if ( it->second.size() > timeStep )
{
return &( it->second[timeStep] );
}
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<std::vector<double>>* RigNNCData::generatedConnectionScalarResultByName( const QString& nncDataType ) const
{
auto it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
return &( it->second );
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<double>* RigNNCData::generatedConnectionScalarResultByName( const QString& nncDataType,
size_t timeStep ) const
{
auto it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
if ( it->second.size() > timeStep )
{
return &( it->second[timeStep] );
}
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<std::vector<double>>* RigNNCData::generatedConnectionScalarResultByName( const QString& nncDataType )
{
auto it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
return &( it->second );
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double>* RigNNCData::generatedConnectionScalarResultByName( const QString& nncDataType, size_t timeStep )
{
auto it = m_connectionResults.find( nncDataType );
if ( it != m_connectionResults.end() )
{
if ( it->second.size() > timeStep )
{
return &( it->second[timeStep] );
}
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QString> RigNNCData::availableProperties( NNCResultType resultType ) const
{
std::vector<QString> properties;
for ( auto it : m_connectionResults )
{
if ( resultType == NNCResultType::NNC_STATIC && it.second.size() == 1 && it.second[0].size() > 0 &&
isNative( it.first ) )
{
properties.push_back( it.first );
}
else if ( resultType == NNCResultType::NNC_DYNAMIC && it.second.size() > 1 && it.second[0].size() > 0 &&
isNative( it.first ) )
{
properties.push_back( it.first );
}
else if ( resultType == NNCResultType::NNC_GENERATED && !isNative( it.first ) )
{
properties.push_back( it.first );
}
}
return properties;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigNNCData::setEclResultAddress( const QString& nncDataType, const RigEclipseResultAddress& resVarAddr )
{
m_resultAddrToNNCDataType[resVarAddr] = nncDataType;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RigNNCData::hasScalarValues( const RigEclipseResultAddress& resVarAddr )
{
QString nncDataType = getNNCDataTypeFromScalarResultIndex( resVarAddr );
if ( nncDataType.isNull() ) return false;
auto it = m_connectionResults.find( nncDataType );
return ( it != m_connectionResults.end() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RigNNCData::generateScalarValues( const RigEclipseResultAddress& resVarAddr )
{
if ( hasScalarValues( resVarAddr ) ) return true;
if ( resVarAddr.isDivideByCellFaceAreaActive() && resVarAddr.resultCatType() == RiaDefines::ResultCatType::DYNAMIC_NATIVE )
{
RigEclipseResultAddress tmpAddr = resVarAddr;
tmpAddr.enableDivideByCellFaceArea( false );
auto nameit = m_resultAddrToNNCDataType.find( tmpAddr );
if ( nameit == m_resultAddrToNNCDataType.end() ) return false;
auto it = m_connectionResults.find( nameit->second );
if ( it == m_connectionResults.end() ) return false;
// Connection polygons are used to compute the center for the NNC flow vectors
// If connection polygons are present, this is a no-op
buildPolygonsForEclipseConnections();
auto& srcdata = it->second;
auto& dstdata = makeDynamicConnectionScalarResult( resVarAddr.resultName(), srcdata.size() );
const double epsilon = 1.0e-3;
std::vector<double> areas( m_connections.size() );
for ( size_t dataIdx = 0; dataIdx < m_connections.size(); dataIdx++ )
{
double area = 0.0;
if ( m_connections[dataIdx].hasCommonArea() )
area = cvf::GeometryTools::polygonArea( m_connections[dataIdx].polygon() );
areas[dataIdx] = area;
}
#pragma omp parallel for
for ( int i = 0; i < static_cast<int>( srcdata.size() ); i++ )
{
size_t timeIdx = i;
dstdata[timeIdx].resize( srcdata[timeIdx].size() );
for ( size_t dataIdx = 0; dataIdx < srcdata[timeIdx].size(); dataIdx++ )
{
double scaledVal = 0.0;
if ( areas[dataIdx] > epsilon ) scaledVal = srcdata[timeIdx][dataIdx] / areas[dataIdx];
dstdata[timeIdx][dataIdx] = scaledVal;
}
}
m_resultAddrToNNCDataType[resVarAddr] = resVarAddr.resultName();
return true;
}
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const QString RigNNCData::getNNCDataTypeFromScalarResultIndex( const RigEclipseResultAddress& resVarAddr ) const
{
auto it = m_resultAddrToNNCDataType.find( resVarAddr );
if ( it != m_resultAddrToNNCDataType.end() )
{
return it->second;
}
return QString();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RigNNCData::isNative( QString nncDataType ) const
{
if ( nncDataType == RiaDefines::propertyNameCombTrans() || nncDataType == RiaDefines::propertyNameFluxGas() ||
nncDataType == RiaDefines::propertyNameFluxOil() || nncDataType == RiaDefines::propertyNameFluxWat() ||
nncDataType == RiaDefines::propertyNameRiCombMult() || nncDataType == RiaDefines::propertyNameRiCombTrans() ||
nncDataType == RiaDefines::propertyNameRiCombTransByArea() )
{
return true;
}
return false;
}