mirror of
https://github.com/OPM/ResInsight.git
synced 2025-01-08 15:14:07 -06:00
973 lines
41 KiB
C++
973 lines
41 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2017 Statoil 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 <http://www.gnu.org/licenses/gpl.html>
|
|
// for more details.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "RigAccWellFlowCalculator.h"
|
|
|
|
#include "RiaWellFlowDefines.h"
|
|
|
|
#include "RigActiveCellInfo.h"
|
|
#include "RigFlowDiagResults.h"
|
|
#include "RigMainGrid.h"
|
|
#include "RigSimWellData.h"
|
|
#include "RigSimulationWellCoordsAndMD.h"
|
|
#include "RigWellResultPoint.h"
|
|
|
|
#include <cmath>
|
|
|
|
//==================================================================================================
|
|
///
|
|
///
|
|
//==================================================================================================
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
size_t RigEclCellIndexCalculator::resultCellIndex( size_t gridIndex, size_t gridCellIndex ) const
|
|
{
|
|
size_t reservoirCellIndex = m_mainGrid->reservoirCellIndexByGridAndGridLocalCellIndex( gridIndex, gridCellIndex );
|
|
|
|
return m_activeCellInfo->cellResultIndex( reservoirCellIndex );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RigEclCellIndexCalculator::isCellVisible( size_t gridIndex, size_t gridCellIndex ) const
|
|
{
|
|
if ( !m_cellVisibilities ) return true;
|
|
|
|
size_t reservoirCellIndex = m_mainGrid->reservoirCellIndexByGridAndGridLocalCellIndex( gridIndex, gridCellIndex );
|
|
|
|
return ( *m_cellVisibilities )[reservoirCellIndex];
|
|
}
|
|
|
|
//==================================================================================================
|
|
///
|
|
///
|
|
//==================================================================================================
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
/// The pipeBranchesWellResultPoints are describing the lines between the points, starting with the first line
|
|
// and is thus expected to be one less than the number of centerline points
|
|
//--------------------------------------------------------------------------------------------------
|
|
RigAccWellFlowCalculator::RigAccWellFlowCalculator( const std::vector<std::vector<cvf::Vec3d>>& pipeBranchesCLCoords,
|
|
const std::vector<std::vector<RigWellResultPoint>>& pipeBranchesWellResultPoints,
|
|
const std::map<QString, const std::vector<double>*>& tracerCellFractionValues,
|
|
const RigEclCellIndexCalculator& cellIndexCalculator,
|
|
double smallContribThreshold,
|
|
bool isProducer )
|
|
: m_pipeBranchesCLCoords( pipeBranchesCLCoords )
|
|
, m_pipeBranchesWellResultPoints( pipeBranchesWellResultPoints )
|
|
, m_tracerCellFractionValues( &tracerCellFractionValues )
|
|
, m_cellIndexCalculator( cellIndexCalculator )
|
|
, m_smallContributionsThreshold( smallContribThreshold )
|
|
, m_isProducer( isProducer )
|
|
, m_useTotalWellPhaseRateOnly( false )
|
|
{
|
|
CVF_ASSERT( !pipeBranchesWellResultPoints.empty() );
|
|
|
|
m_connectionFlowPrBranch.resize( m_pipeBranchesWellResultPoints.size() );
|
|
m_pseudoLengthFlowPrBranch.resize( m_pipeBranchesWellResultPoints.size() );
|
|
|
|
for ( const auto& it : ( *m_tracerCellFractionValues ) )
|
|
m_tracerNames.push_back( it.first );
|
|
|
|
m_tracerNames.push_back( RiaDefines::reservoirTracerName() );
|
|
|
|
initializePipeBranchesMeasuredDepths();
|
|
calculateFlowData();
|
|
sortTracers();
|
|
groupSmallContributions();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RigAccWellFlowCalculator::RigAccWellFlowCalculator( const std::vector<std::vector<cvf::Vec3d>>& pipeBranchesCLCoords,
|
|
const std::vector<std::vector<RigWellResultPoint>>& pipeBranchesWellResultPoints,
|
|
double smallContribThreshold )
|
|
: m_pipeBranchesCLCoords( pipeBranchesCLCoords )
|
|
, m_pipeBranchesWellResultPoints( pipeBranchesWellResultPoints )
|
|
, m_tracerCellFractionValues( nullptr )
|
|
, m_cellIndexCalculator( RigEclCellIndexCalculator( nullptr, nullptr, nullptr ) )
|
|
, m_smallContributionsThreshold( smallContribThreshold )
|
|
, m_isProducer( true )
|
|
, m_useTotalWellPhaseRateOnly( false )
|
|
{
|
|
CVF_ASSERT( !pipeBranchesWellResultPoints.empty() );
|
|
|
|
m_connectionFlowPrBranch.resize( m_pipeBranchesWellResultPoints.size() );
|
|
m_pseudoLengthFlowPrBranch.resize( m_pipeBranchesWellResultPoints.size() );
|
|
|
|
if ( !m_useTotalWellPhaseRateOnly )
|
|
{
|
|
m_tracerNames.push_back( RIG_FLOW_OIL_NAME );
|
|
m_tracerNames.push_back( RIG_FLOW_GAS_NAME );
|
|
m_tracerNames.push_back( RIG_FLOW_WATER_NAME );
|
|
}
|
|
else
|
|
{
|
|
m_tracerNames.push_back( RIG_FLOW_TOTAL_NAME );
|
|
}
|
|
|
|
initializePipeBranchesMeasuredDepths();
|
|
calculateFlowData();
|
|
|
|
if ( !m_useTotalWellPhaseRateOnly ) sortTracers();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RigAccWellFlowCalculator::RigAccWellFlowCalculator( const std::vector<cvf::Vec3d>& pipeBranchCLCoords,
|
|
const std::vector<RigWellResultPoint>& pipeBranchesWellResultPoints,
|
|
const std::vector<double>& pipeBranchMeasuredDepths,
|
|
bool totalFlowOnly )
|
|
: m_tracerCellFractionValues( nullptr )
|
|
, m_cellIndexCalculator( RigEclCellIndexCalculator( nullptr, nullptr, nullptr ) )
|
|
, m_smallContributionsThreshold( 0.0 )
|
|
, m_isProducer( true )
|
|
, m_useTotalWellPhaseRateOnly( totalFlowOnly )
|
|
{
|
|
CVF_ASSERT( !pipeBranchesWellResultPoints.empty() );
|
|
|
|
m_pipeBranchesCLCoords.push_back( pipeBranchCLCoords );
|
|
m_pipeBranchesWellResultPoints.push_back( pipeBranchesWellResultPoints );
|
|
m_pipeBranchesMeasuredDepths.push_back( pipeBranchMeasuredDepths );
|
|
|
|
m_connectionFlowPrBranch.resize( m_pipeBranchesWellResultPoints.size() );
|
|
m_pseudoLengthFlowPrBranch.resize( m_pipeBranchesWellResultPoints.size() );
|
|
|
|
if ( !m_useTotalWellPhaseRateOnly )
|
|
{
|
|
m_tracerNames.push_back( RIG_FLOW_OIL_NAME );
|
|
m_tracerNames.push_back( RIG_FLOW_GAS_NAME );
|
|
m_tracerNames.push_back( RIG_FLOW_WATER_NAME );
|
|
}
|
|
else
|
|
{
|
|
m_tracerNames.push_back( RIG_FLOW_TOTAL_NAME );
|
|
}
|
|
|
|
initializePipeBranchesMeasuredDepths();
|
|
calculateFlowData();
|
|
|
|
if ( !m_useTotalWellPhaseRateOnly ) sortTracers();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RigAccWellFlowCalculator::initializePipeBranchesMeasuredDepths()
|
|
{
|
|
for ( const auto& branchClPoints : m_pipeBranchesCLCoords )
|
|
{
|
|
RigSimulationWellCoordsAndMD mdCalculator( branchClPoints );
|
|
m_pipeBranchesMeasuredDepths.push_back( mdCalculator.measuredDepths() );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const std::vector<double>& RigAccWellFlowCalculator::connectionNumbersFromTop( size_t branchIdx ) const
|
|
{
|
|
return m_connectionFlowPrBranch[branchIdx].depthValuesFromTop;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const std::vector<double>& RigAccWellFlowCalculator::accumulatedTracerFlowPrConnection( const QString& tracerName, size_t branchIdx ) const
|
|
{
|
|
auto flowPrTracerIt = m_connectionFlowPrBranch[branchIdx].accFlowPrTracer.find( tracerName );
|
|
if ( flowPrTracerIt != m_connectionFlowPrBranch[branchIdx].accFlowPrTracer.end() )
|
|
{
|
|
return flowPrTracerIt->second;
|
|
}
|
|
else
|
|
{
|
|
CVF_ASSERT( false );
|
|
static std::vector<double> dummy;
|
|
return dummy;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const std::vector<double>& RigAccWellFlowCalculator::tracerFlowPrConnection( const QString& tracerName, size_t branchIdx ) const
|
|
{
|
|
auto flowPrTracerIt = m_connectionFlowPrBranch[branchIdx].flowPrTracer.find( tracerName );
|
|
if ( flowPrTracerIt != m_connectionFlowPrBranch[branchIdx].flowPrTracer.end() )
|
|
{
|
|
return flowPrTracerIt->second;
|
|
}
|
|
else
|
|
{
|
|
CVF_ASSERT( false );
|
|
static std::vector<double> dummy;
|
|
return dummy;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const std::vector<double>& RigAccWellFlowCalculator::pseudoLengthFromTop( size_t branchIdx ) const
|
|
{
|
|
return m_pseudoLengthFlowPrBranch[branchIdx].depthValuesFromTop;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const std::vector<double>& RigAccWellFlowCalculator::trueVerticalDepth( size_t branchIdx ) const
|
|
{
|
|
return m_pseudoLengthFlowPrBranch[branchIdx].trueVerticalDepth;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const std::vector<double>& RigAccWellFlowCalculator::accumulatedTracerFlowPrPseudoLength( const QString& tracerName, size_t branchIdx ) const
|
|
{
|
|
auto flowPrTracerIt = m_pseudoLengthFlowPrBranch[branchIdx].accFlowPrTracer.find( tracerName );
|
|
if ( flowPrTracerIt != m_pseudoLengthFlowPrBranch[branchIdx].accFlowPrTracer.end() )
|
|
{
|
|
return flowPrTracerIt->second;
|
|
}
|
|
else
|
|
{
|
|
CVF_ASSERT( false );
|
|
static std::vector<double> dummy;
|
|
return dummy;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const std::vector<double>& RigAccWellFlowCalculator::tracerFlowPrPseudoLength( const QString& tracerName, size_t branchIdx ) const
|
|
{
|
|
auto flowPrTracerIt = m_pseudoLengthFlowPrBranch[branchIdx].flowPrTracer.find( tracerName );
|
|
if ( flowPrTracerIt != m_pseudoLengthFlowPrBranch[branchIdx].flowPrTracer.end() )
|
|
{
|
|
return flowPrTracerIt->second;
|
|
}
|
|
else
|
|
{
|
|
CVF_ASSERT( false );
|
|
static std::vector<double> dummy;
|
|
return dummy;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<std::pair<QString, double>> RigAccWellFlowCalculator::totalWellFlowPrTracer() const
|
|
{
|
|
std::vector<QString> tracerNames = this->tracerNames();
|
|
std::vector<std::pair<QString, double>> tracerWithValues;
|
|
|
|
for ( const QString& tracerName : tracerNames )
|
|
{
|
|
const std::vector<double>& accFlow = this->accumulatedTracerFlowPrConnection( tracerName, 0 );
|
|
tracerWithValues.push_back( std::make_pair( tracerName, accFlow.back() ) );
|
|
}
|
|
|
|
return tracerWithValues;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<std::pair<QString, double>> RigAccWellFlowCalculator::totalTracerFractions() const
|
|
{
|
|
std::vector<std::pair<QString, double>> totalFlows = totalWellFlowPrTracer();
|
|
|
|
float sumTracerFlows = 0.0f;
|
|
for ( const auto& tracerVal : totalFlows )
|
|
{
|
|
sumTracerFlows += tracerVal.second;
|
|
}
|
|
|
|
if ( sumTracerFlows == 0.0 ) totalFlows.clear();
|
|
|
|
for ( auto& tracerPair : totalFlows )
|
|
{
|
|
tracerPair.second = tracerPair.second / sumTracerFlows;
|
|
}
|
|
|
|
return totalFlows;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RigAccWellFlowCalculator::isWellFlowConsistent() const
|
|
{
|
|
bool isConsistent = true;
|
|
for ( const std::vector<RigWellResultPoint>& branch : m_pipeBranchesWellResultPoints )
|
|
{
|
|
for ( const RigWellResultPoint& wrp : branch )
|
|
{
|
|
isConsistent = isFlowRateConsistent( wrp.flowRate() );
|
|
|
|
if ( !isConsistent ) break;
|
|
}
|
|
if ( !isConsistent ) break;
|
|
}
|
|
return isConsistent;
|
|
}
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
std::vector<double> RigAccWellFlowCalculator::calculateAccumulatedFractions( const std::vector<double>& accumulatedFlowPrTracer ) const
|
|
{
|
|
double totalFlow = 0.0;
|
|
for ( double tracerFlow : accumulatedFlowPrTracer )
|
|
{
|
|
totalFlow += tracerFlow;
|
|
}
|
|
|
|
std::vector<double> flowFractionsPrTracer( accumulatedFlowPrTracer.size(), 0.0 );
|
|
|
|
if ( totalFlow == 0.0 || !isFlowRateConsistent( totalFlow ) ) // If we have no accumulated flow, we set all the flow
|
|
// associated to the last tracer, which is the reservoir
|
|
{
|
|
flowFractionsPrTracer.back() = 1.0;
|
|
return flowFractionsPrTracer;
|
|
}
|
|
|
|
for ( size_t tIdx = 0; tIdx < accumulatedFlowPrTracer.size(); ++tIdx )
|
|
{
|
|
double tracerFlow = accumulatedFlowPrTracer[tIdx];
|
|
flowFractionsPrTracer[tIdx] = tracerFlow / totalFlow;
|
|
}
|
|
|
|
return flowFractionsPrTracer;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RigAccWellFlowCalculator::isConnectionFlowConsistent( const RigWellResultPoint& wellCell ) const
|
|
{
|
|
if ( !m_tracerCellFractionValues ) return true; // No flow diagnostics.
|
|
|
|
return isFlowRateConsistent( wellCell.flowRate() );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RigAccWellFlowCalculator::isFlowRateConsistent( double flowRate ) const
|
|
{
|
|
if ( !m_tracerCellFractionValues ) return true; // No flow diagnostics.
|
|
|
|
return ( flowRate >= 0.0 && m_isProducer ) || ( flowRate <= 0.0 && !m_isProducer );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RigAccWellFlowCalculator::calculateFlowData()
|
|
{
|
|
for ( size_t branchIndex = 0; branchIndex < m_connectionFlowPrBranch.size(); branchIndex++ )
|
|
{
|
|
const auto branch = m_connectionFlowPrBranch[branchIndex];
|
|
if ( branch.depthValuesFromTop.empty() )
|
|
{
|
|
calculateAccumulatedFlowPrConnection( branchIndex, 0 );
|
|
}
|
|
}
|
|
|
|
for ( size_t branchIndex = 0; branchIndex < m_pseudoLengthFlowPrBranch.size(); branchIndex++ )
|
|
{
|
|
const auto branch = m_pseudoLengthFlowPrBranch[branchIndex];
|
|
if ( branch.depthValuesFromTop.empty() )
|
|
{
|
|
calculateFlowPrPseudoLength( branchIndex, 0.0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RigAccWellFlowCalculator::calculateAccumulatedFlowPrConnection( size_t branchIdx, size_t startConnectionNumberFromTop )
|
|
{
|
|
const std::vector<RigWellResultPoint>& branchCells = m_pipeBranchesWellResultPoints[branchIdx];
|
|
|
|
std::vector<size_t> resPointUniqueIndexFromBottom = wrpToUniqueWrpIndexFromBottom( branchCells );
|
|
|
|
size_t prevConnIndx = -1;
|
|
int clSegIdx = static_cast<int>( branchCells.size() ) - 1;
|
|
|
|
std::vector<double> accFlowPrTracer( m_tracerNames.size(), 0.0 );
|
|
|
|
while ( clSegIdx >= 0 )
|
|
{
|
|
// Skip point if referring to the same cell as the previous centerline segment did
|
|
{
|
|
if ( resPointUniqueIndexFromBottom[clSegIdx] == prevConnIndx )
|
|
{
|
|
--clSegIdx;
|
|
continue;
|
|
}
|
|
|
|
prevConnIndx = resPointUniqueIndexFromBottom[clSegIdx];
|
|
}
|
|
|
|
// Accumulate the connection-cell's fraction flows
|
|
|
|
const RigWellResultPoint& wellCell = branchCells[clSegIdx];
|
|
|
|
std::vector<double> flowPrTracer = calculateWellCellFlowPrTracer( wellCell, accFlowPrTracer );
|
|
|
|
addDownStreamBranchFlow( &accFlowPrTracer, flowPrTracer );
|
|
|
|
if ( !isConnectionFlowConsistent( wellCell ) )
|
|
{
|
|
// Associate all the flow with the reservoir tracer for inconsistent flow direction
|
|
flowPrTracer = std::vector<double>( flowPrTracer.size(), 0.0 );
|
|
flowPrTracer.back() = wellCell.flowRate();
|
|
}
|
|
|
|
// Add the total accumulated (fraction) flows from any branches connected to this cell
|
|
|
|
size_t connNumFromTop = connectionIndexFromTop( resPointUniqueIndexFromBottom, clSegIdx ) + startConnectionNumberFromTop;
|
|
|
|
std::vector<size_t> downStreamBranchIndices = findDownStreamBranchIdxs( branchCells[clSegIdx] );
|
|
for ( size_t dsBidx : downStreamBranchIndices )
|
|
{
|
|
BranchFlow& downStreamBranchFlow = m_connectionFlowPrBranch[dsBidx];
|
|
if ( dsBidx != branchIdx && downStreamBranchFlow.depthValuesFromTop.size() == 0 ) // Not this branch or
|
|
// already calculated
|
|
{
|
|
calculateAccumulatedFlowPrConnection( dsBidx, connNumFromTop );
|
|
std::vector<double> accBranchFlowPrTracer = accumulatedDsBranchFlowPrTracer( downStreamBranchFlow );
|
|
addDownStreamBranchFlow( &accFlowPrTracer, accBranchFlowPrTracer );
|
|
if ( m_pipeBranchesWellResultPoints[dsBidx].size() <= 3 )
|
|
{
|
|
// Short branch. Will not be visible. Show branch flow as addition to this connections direct flow
|
|
addDownStreamBranchFlow( &flowPrTracer, accBranchFlowPrTracer );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Push back the accumulated result into the storage
|
|
|
|
BranchFlow& branchFlow = m_connectionFlowPrBranch[branchIdx];
|
|
|
|
storeFlowOnDepth( &branchFlow, connNumFromTop, accFlowPrTracer, flowPrTracer );
|
|
|
|
--clSegIdx;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RigAccWellFlowCalculator::calculateFlowPrPseudoLength( size_t branchIdx, double startPseudoLengthFromTop )
|
|
{
|
|
const std::vector<RigWellResultPoint>& branchCells = m_pipeBranchesWellResultPoints[branchIdx];
|
|
const std::vector<cvf::Vec3d>& branchClPoints = m_pipeBranchesCLCoords[branchIdx];
|
|
const std::vector<double>& branchMDs = m_pipeBranchesMeasuredDepths[branchIdx];
|
|
|
|
int clSegIdx = static_cast<int>( branchCells.size() ) - 1;
|
|
|
|
std::vector<double> accFlowPrTracer( m_tracerNames.size(), 0.0 );
|
|
|
|
BranchFlow& branchFlow = m_pseudoLengthFlowPrBranch[branchIdx];
|
|
|
|
RigWellResultPoint previousResultPoint;
|
|
|
|
while ( clSegIdx >= 0 )
|
|
{
|
|
int cellBottomPointIndex = -1;
|
|
int cellUpperPointIndex = -1;
|
|
int currentSegmentIndex = -1;
|
|
|
|
// Find the complete cell span
|
|
{
|
|
cellBottomPointIndex = clSegIdx + 1;
|
|
|
|
previousResultPoint = branchCells[clSegIdx];
|
|
--clSegIdx;
|
|
while ( clSegIdx >= 0 && previousResultPoint.isEqual( branchCells[clSegIdx] ) )
|
|
{
|
|
--clSegIdx;
|
|
}
|
|
|
|
cellUpperPointIndex = clSegIdx + 1;
|
|
currentSegmentIndex = cellUpperPointIndex;
|
|
}
|
|
const RigWellResultPoint& wellCell = branchCells[currentSegmentIndex];
|
|
std::vector<double> flowPrTracerToAccumulate = calculateWellCellFlowPrTracer( wellCell, accFlowPrTracer );
|
|
|
|
double pseudoLengthFromTop_lower = branchMDs[cellBottomPointIndex] + startPseudoLengthFromTop;
|
|
double tvd_lower = -branchClPoints[cellBottomPointIndex][2];
|
|
|
|
// Push back the new start-of-cell flow, with the previously accumulated result into the storage
|
|
|
|
std::vector<double> flowPrTracer;
|
|
if ( !isConnectionFlowConsistent( wellCell ) )
|
|
{
|
|
// Associate all the flow with the reservoir tracer for inconsistent flow direction
|
|
flowPrTracer = std::vector<double>( flowPrTracerToAccumulate.size(), 0.0 );
|
|
flowPrTracer.back() = wellCell.flowRate();
|
|
}
|
|
else
|
|
{
|
|
flowPrTracer = flowPrTracerToAccumulate;
|
|
}
|
|
|
|
storeFlowOnDepthWTvd( &branchFlow, pseudoLengthFromTop_lower, tvd_lower, accFlowPrTracer, flowPrTracer );
|
|
|
|
// Accumulate the connection-cell's fraction flows
|
|
|
|
addDownStreamBranchFlow( &accFlowPrTracer, flowPrTracerToAccumulate );
|
|
|
|
double pseudoLengthFromTop_upper = branchMDs[cellUpperPointIndex] + startPseudoLengthFromTop;
|
|
double tvd_upper = -branchClPoints[cellUpperPointIndex][2];
|
|
|
|
// Push back the accumulated result into the storage
|
|
|
|
storeFlowOnDepthWTvd( &branchFlow, pseudoLengthFromTop_upper, tvd_upper, accFlowPrTracer, flowPrTracer );
|
|
|
|
// Add the total accumulated (fraction) flows from any branches connected to this cell
|
|
|
|
std::vector<size_t> downStreamBranchIndices = findDownStreamBranchIdxs( branchCells[cellUpperPointIndex] );
|
|
for ( size_t dsBidx : downStreamBranchIndices )
|
|
{
|
|
BranchFlow& downStreamBranchFlow = m_pseudoLengthFlowPrBranch[dsBidx];
|
|
if ( dsBidx != branchIdx && downStreamBranchFlow.depthValuesFromTop.size() == 0 ) // Not this branch or
|
|
// already calculated
|
|
{
|
|
calculateFlowPrPseudoLength( dsBidx, pseudoLengthFromTop_upper );
|
|
std::vector<double> accBranchFlowPrTracer = accumulatedDsBranchFlowPrTracer( downStreamBranchFlow );
|
|
addDownStreamBranchFlow( &accFlowPrTracer, accBranchFlowPrTracer );
|
|
if ( m_pipeBranchesWellResultPoints[dsBidx].size() <= 3 )
|
|
{
|
|
// Short branch. Will not be visible. Show branch flow as addition to this connections direct flow
|
|
addDownStreamBranchFlow( &flowPrTracer, accBranchFlowPrTracer );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Push back the accumulated result after adding the branch result into the storage
|
|
|
|
if ( downStreamBranchIndices.size() )
|
|
storeFlowOnDepthWTvd( &branchFlow, pseudoLengthFromTop_upper, tvd_upper, accFlowPrTracer, flowPrTracer );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RigAccWellFlowCalculator::addDownStreamBranchFlow( std::vector<double>* accFlowPrTracer,
|
|
const std::vector<double>& accBranchFlowPrTracer ) const
|
|
{
|
|
double totalThisBranchFlow = 0.0;
|
|
for ( double tracerFlow : *accFlowPrTracer )
|
|
{
|
|
totalThisBranchFlow += tracerFlow;
|
|
}
|
|
|
|
double totalDsBranchFlow = 0.0;
|
|
for ( double tracerFlow : accBranchFlowPrTracer )
|
|
{
|
|
totalDsBranchFlow += tracerFlow;
|
|
}
|
|
|
|
bool isAccumulationConsistent = isFlowRateConsistent( totalThisBranchFlow ); // If inconsistent, is it always only
|
|
// the Reservoir tracer that has the
|
|
// flow ?
|
|
bool isBranchConsistent = isFlowRateConsistent( totalDsBranchFlow );
|
|
|
|
if ( isAccumulationConsistent == isBranchConsistent )
|
|
{
|
|
for ( size_t tracerIdx = 0; tracerIdx < ( *accFlowPrTracer ).size(); ++tracerIdx )
|
|
{
|
|
( *accFlowPrTracer )[tracerIdx] += accBranchFlowPrTracer[tracerIdx];
|
|
}
|
|
return;
|
|
}
|
|
|
|
double totalAccFlow = totalThisBranchFlow + totalDsBranchFlow;
|
|
|
|
if ( !isFlowRateConsistent( totalAccFlow ) )
|
|
{
|
|
// Reset the accumulated values, as everything must be moved to the "Reservoir" tracer.
|
|
for ( double& val : ( *accFlowPrTracer ) )
|
|
val = 0.0;
|
|
|
|
// Put all flow into the Reservoir tracer
|
|
accFlowPrTracer->back() = totalThisBranchFlow + totalDsBranchFlow;
|
|
|
|
return;
|
|
}
|
|
|
|
// We will end up with a consistent accumulated flow, and need to keep the accumulated distribution in this branch
|
|
// or to use the ds branch distribution
|
|
|
|
std::vector<double> accFractionsPrTracer;
|
|
|
|
if ( !isAccumulationConsistent && isBranchConsistent )
|
|
{
|
|
accFractionsPrTracer = calculateAccumulatedFractions( accBranchFlowPrTracer );
|
|
}
|
|
else if ( isAccumulationConsistent && !isBranchConsistent )
|
|
{
|
|
accFractionsPrTracer = calculateAccumulatedFractions( *accFlowPrTracer );
|
|
}
|
|
|
|
// Set the accumulated values to the totalFlow times the tracer fraction selected.
|
|
|
|
for ( size_t tIdx = 0; tIdx < accFlowPrTracer->size(); ++tIdx )
|
|
{
|
|
( *accFlowPrTracer )[tIdx] = accFractionsPrTracer[tIdx] * ( totalAccFlow );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RigAccWellFlowCalculator::storeFlowOnDepth( BranchFlow* branchFlow,
|
|
double depthValue,
|
|
const std::vector<double>& accFlowPrTracer,
|
|
const std::vector<double>& flowPrTracer )
|
|
{
|
|
size_t tracerIdx = 0;
|
|
for ( const auto& tracerName : m_tracerNames )
|
|
{
|
|
branchFlow->accFlowPrTracer[tracerName].push_back( accFlowPrTracer[tracerIdx] );
|
|
branchFlow->flowPrTracer[tracerName].push_back( flowPrTracer[tracerIdx] );
|
|
tracerIdx++;
|
|
}
|
|
|
|
branchFlow->depthValuesFromTop.push_back( depthValue );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RigAccWellFlowCalculator::storeFlowOnDepthWTvd( BranchFlow* branchFlow,
|
|
double depthValue,
|
|
double trueVerticalDepth,
|
|
const std::vector<double>& accFlowPrTracer,
|
|
const std::vector<double>& flowPrTracer )
|
|
{
|
|
size_t tracerIdx = 0;
|
|
for ( const auto& tracerName : m_tracerNames )
|
|
{
|
|
branchFlow->accFlowPrTracer[tracerName].push_back( accFlowPrTracer[tracerIdx] );
|
|
branchFlow->flowPrTracer[tracerName].push_back( flowPrTracer[tracerIdx] );
|
|
tracerIdx++;
|
|
}
|
|
|
|
branchFlow->depthValuesFromTop.push_back( depthValue );
|
|
branchFlow->trueVerticalDepth.push_back( trueVerticalDepth );
|
|
}
|
|
|
|
std::vector<double> RigAccWellFlowCalculator::accumulatedDsBranchFlowPrTracer( const BranchFlow& downStreamBranchFlow ) const
|
|
{
|
|
std::vector<double> accBranchFlowPrTracer( m_tracerNames.size(), 0.0 );
|
|
|
|
size_t tracerIdx = 0;
|
|
for ( const auto& tracerName : m_tracerNames )
|
|
{
|
|
const auto trNameAccFlowsPair = downStreamBranchFlow.accFlowPrTracer.find( tracerName );
|
|
if ( trNameAccFlowsPair != downStreamBranchFlow.accFlowPrTracer.end() )
|
|
{
|
|
accBranchFlowPrTracer[tracerIdx] = trNameAccFlowsPair->second.back();
|
|
}
|
|
tracerIdx++;
|
|
}
|
|
|
|
return accBranchFlowPrTracer;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Calculate the flow pr tracer. If inconsistent flow, keep the existing fractions constant
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<double> RigAccWellFlowCalculator::calculateWellCellFlowPrTracer( const RigWellResultPoint& wellCell,
|
|
const std::vector<double>& currentAccumulatedFlowPrTracer ) const
|
|
{
|
|
std::vector<double> flowPrTracer( m_tracerNames.size(), 0.0 );
|
|
|
|
if ( wellCell.isCell() && !m_cellIndexCalculator.isCellVisible( wellCell.gridIndex(), wellCell.cellIndex() ) ) return flowPrTracer;
|
|
|
|
if ( !isConnectionFlowConsistent( wellCell ) )
|
|
{
|
|
double flowRate = wellCell.flowRate();
|
|
flowPrTracer = calculateAccumulatedFractions( currentAccumulatedFlowPrTracer );
|
|
for ( double& accFraction : flowPrTracer )
|
|
{
|
|
accFraction *= flowRate;
|
|
}
|
|
|
|
return flowPrTracer;
|
|
}
|
|
|
|
if ( m_tracerCellFractionValues )
|
|
{
|
|
if ( wellCell.isCell() && wellCell.isOpen() )
|
|
{
|
|
size_t resCellIndex = m_cellIndexCalculator.resultCellIndex( wellCell.gridIndex(), wellCell.cellIndex() );
|
|
size_t tracerIdx = 0;
|
|
double totalTracerFractionInCell = 0.0;
|
|
for ( const auto& tracerFractionValsPair : ( *m_tracerCellFractionValues ) )
|
|
{
|
|
const std::vector<double>* fractionVals = tracerFractionValsPair.second;
|
|
if ( fractionVals )
|
|
{
|
|
double cellTracerFraction = ( *fractionVals )[resCellIndex];
|
|
if ( cellTracerFraction != HUGE_VAL && cellTracerFraction == cellTracerFraction )
|
|
{
|
|
double tracerFlow = cellTracerFraction * wellCell.flowRate();
|
|
flowPrTracer[tracerIdx] = tracerFlow;
|
|
|
|
totalTracerFractionInCell += cellTracerFraction;
|
|
}
|
|
}
|
|
tracerIdx++;
|
|
}
|
|
|
|
double reservoirFraction = 1.0 - totalTracerFractionInCell;
|
|
double reservoirTracerFlow = reservoirFraction * wellCell.flowRate();
|
|
flowPrTracer[tracerIdx] = reservoirTracerFlow;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !m_useTotalWellPhaseRateOnly )
|
|
{
|
|
flowPrTracer[0] = wellCell.oilRate();
|
|
flowPrTracer[1] = wellCell.gasRate();
|
|
flowPrTracer[2] = wellCell.waterRate();
|
|
}
|
|
else
|
|
{
|
|
flowPrTracer[0] = wellCell.flowRate();
|
|
}
|
|
}
|
|
|
|
return flowPrTracer;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<size_t> RigAccWellFlowCalculator::wrpToUniqueWrpIndexFromBottom( const std::vector<RigWellResultPoint>& branchCells ) const
|
|
{
|
|
std::vector<size_t> resPointToConnectionIndexFromBottom;
|
|
resPointToConnectionIndexFromBottom.resize( branchCells.size(), -1 );
|
|
|
|
size_t connIdxFromBottom = 0;
|
|
int clSegIdx = static_cast<int>( branchCells.size() ) - 1;
|
|
|
|
if ( clSegIdx < 0 ) return resPointToConnectionIndexFromBottom;
|
|
|
|
size_t prevGridIdx = branchCells[clSegIdx].gridIndex();
|
|
size_t prevGridCellIdx = branchCells[clSegIdx].cellIndex();
|
|
int prevErtSegId = branchCells[clSegIdx].segmentId();
|
|
int prevErtBranchId = branchCells[clSegIdx].branchId();
|
|
|
|
while ( clSegIdx >= 0 )
|
|
{
|
|
if ( branchCells[clSegIdx].isValid() &&
|
|
( branchCells[clSegIdx].gridIndex() != prevGridIdx || branchCells[clSegIdx].cellIndex() != prevGridCellIdx ||
|
|
branchCells[clSegIdx].segmentId() != prevErtSegId || branchCells[clSegIdx].branchId() != prevErtBranchId ) )
|
|
{
|
|
++connIdxFromBottom;
|
|
|
|
prevGridIdx = branchCells[clSegIdx].gridIndex();
|
|
prevGridCellIdx = branchCells[clSegIdx].cellIndex();
|
|
prevErtSegId = branchCells[clSegIdx].segmentId();
|
|
prevErtBranchId = branchCells[clSegIdx].branchId();
|
|
}
|
|
|
|
resPointToConnectionIndexFromBottom[clSegIdx] = connIdxFromBottom;
|
|
|
|
--clSegIdx;
|
|
}
|
|
|
|
return resPointToConnectionIndexFromBottom;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
size_t RigAccWellFlowCalculator::connectionIndexFromTop( const std::vector<size_t>& resPointToConnectionIndexFromBottom, size_t clSegIdx )
|
|
{
|
|
return resPointToConnectionIndexFromBottom.front() - resPointToConnectionIndexFromBottom[clSegIdx];
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<size_t> RigAccWellFlowCalculator::findDownStreamBranchIdxs( const RigWellResultPoint& connectionPoint ) const
|
|
{
|
|
std::vector<size_t> downStreamBranchIdxs;
|
|
|
|
for ( size_t bIdx = 0; bIdx < m_pipeBranchesWellResultPoints.size(); ++bIdx )
|
|
{
|
|
if ( m_pipeBranchesWellResultPoints[bIdx][0].gridIndex() == connectionPoint.gridIndex() &&
|
|
m_pipeBranchesWellResultPoints[bIdx][0].cellIndex() == connectionPoint.cellIndex() &&
|
|
m_pipeBranchesWellResultPoints[bIdx][0].branchId() == connectionPoint.branchId() &&
|
|
m_pipeBranchesWellResultPoints[bIdx][0].segmentId() == connectionPoint.segmentId() )
|
|
{
|
|
downStreamBranchIdxs.push_back( bIdx );
|
|
}
|
|
}
|
|
return downStreamBranchIdxs;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
void RigAccWellFlowCalculator::sortTracers()
|
|
{
|
|
std::multimap<double, QString> sortedTracers;
|
|
for ( const QString& tracerName : m_tracerNames )
|
|
{
|
|
const std::vector<double>& mainBranchAccFlow = accumulatedTracerFlowPrConnection( tracerName, 0 );
|
|
|
|
double totalFlow = 0.0;
|
|
|
|
if ( mainBranchAccFlow.size() ) totalFlow = -fabs( mainBranchAccFlow.back() ); // Based on size in reverse order (biggest to least)
|
|
|
|
sortedTracers.insert( { totalFlow, tracerName } );
|
|
}
|
|
|
|
m_tracerNames.clear();
|
|
for ( const auto& tracerPair : sortedTracers )
|
|
{
|
|
m_tracerNames.push_back( tracerPair.second );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Concatenate small tracers into an "Other" group
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RigAccWellFlowCalculator::groupSmallContributions()
|
|
{
|
|
if ( !( m_smallContributionsThreshold > 0.0 ) ) return;
|
|
|
|
// Find the tracers we need to group
|
|
|
|
std::vector<QString> tracersToGroup;
|
|
{
|
|
bool hasConsistentWellFlow = isWellFlowConsistent();
|
|
|
|
std::vector<std::pair<QString, double>> totalTracerFractions = this->totalTracerFractions();
|
|
|
|
for ( const auto& tracerPair : totalTracerFractions )
|
|
{
|
|
if ( fabs( tracerPair.second ) <= m_smallContributionsThreshold &&
|
|
( hasConsistentWellFlow || tracerPair.first != RiaDefines::reservoirTracerName() ) ) // Do not group
|
|
// the Reservoir
|
|
// tracer if the
|
|
// well flow is
|
|
// inconsistent,
|
|
// because cross
|
|
// flow is shown
|
|
// as the
|
|
// reservoir
|
|
// fraction
|
|
{
|
|
tracersToGroup.push_back( tracerPair.first );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( tracersToGroup.size() < 2 ) return; // Must at least group two ...
|
|
|
|
// Concatenate the values for each branch, erasing the tracers being grouped, replaced with the concatenated values
|
|
|
|
for ( BranchFlow& brRes : m_connectionFlowPrBranch )
|
|
{
|
|
groupSmallTracers( &brRes.accFlowPrTracer, tracersToGroup );
|
|
groupSmallTracers( &brRes.flowPrTracer, tracersToGroup );
|
|
}
|
|
|
|
for ( BranchFlow& brRes : m_pseudoLengthFlowPrBranch )
|
|
{
|
|
groupSmallTracers( &brRes.accFlowPrTracer, tracersToGroup );
|
|
groupSmallTracers( &brRes.flowPrTracer, tracersToGroup );
|
|
}
|
|
|
|
// Remove the grouped tracer names from the tracerName list, and replace with the "Others" name
|
|
|
|
std::vector<QString> filteredTracernames;
|
|
for ( const QString& tracerName : m_tracerNames )
|
|
{
|
|
bool isDeleted = false;
|
|
for ( const QString& deletedTracerName : tracersToGroup )
|
|
{
|
|
if ( tracerName == deletedTracerName )
|
|
{
|
|
isDeleted = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !isDeleted ) filteredTracernames.push_back( tracerName );
|
|
}
|
|
|
|
m_tracerNames.swap( filteredTracernames );
|
|
m_tracerNames.push_back( RIG_TINY_TRACER_GROUP_NAME );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RigAccWellFlowCalculator::groupSmallTracers( std::map<QString, std::vector<double>>* branchFlowSet,
|
|
const std::vector<QString>& tracersToGroup )
|
|
{
|
|
if ( branchFlowSet->empty() ) return;
|
|
|
|
size_t depthCount = branchFlowSet->begin()->second.size();
|
|
std::vector<double> groupedAccFlowValues( depthCount, 0.0 );
|
|
|
|
for ( const QString& tracername : tracersToGroup )
|
|
{
|
|
auto it = branchFlowSet->find( tracername );
|
|
|
|
if ( it != branchFlowSet->end() )
|
|
{
|
|
const std::vector<double>& tracerVals = it->second;
|
|
for ( size_t cIdx = 0; cIdx < groupedAccFlowValues.size(); ++cIdx )
|
|
{
|
|
groupedAccFlowValues[cIdx] += tracerVals[cIdx];
|
|
}
|
|
}
|
|
|
|
branchFlowSet->erase( it );
|
|
}
|
|
|
|
( *branchFlowSet )[RIG_TINY_TRACER_GROUP_NAME] = groupedAccFlowValues;
|
|
}
|