///////////////////////////////////////////////////////////////////////////////// // // 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 // 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 //================================================================================================== /// /// //================================================================================================== //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- 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>& pipeBranchesCLCoords, const std::vector>& pipeBranchesWellResultPoints, const std::map*>& 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>& pipeBranchesCLCoords, const std::vector>& 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& pipeBranchCLCoords, const std::vector& pipeBranchesWellResultPoints, const std::vector& 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& RigAccWellFlowCalculator::connectionNumbersFromTop( size_t branchIdx ) const { return m_connectionFlowPrBranch[branchIdx].depthValuesFromTop; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& 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 dummy; return dummy; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& 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 dummy; return dummy; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& RigAccWellFlowCalculator::pseudoLengthFromTop( size_t branchIdx ) const { return m_pseudoLengthFlowPrBranch[branchIdx].depthValuesFromTop; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& RigAccWellFlowCalculator::trueVerticalDepth( size_t branchIdx ) const { return m_pseudoLengthFlowPrBranch[branchIdx].trueVerticalDepth; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& 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 dummy; return dummy; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& 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 dummy; return dummy; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector> RigAccWellFlowCalculator::totalWellFlowPrTracer() const { std::vector tracerNames = this->tracerNames(); std::vector> tracerWithValues; for ( const QString& tracerName : tracerNames ) { const std::vector& accFlow = this->accumulatedTracerFlowPrConnection( tracerName, 0 ); tracerWithValues.push_back( std::make_pair( tracerName, accFlow.back() ) ); } return tracerWithValues; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector> RigAccWellFlowCalculator::totalTracerFractions() const { std::vector> 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& branch : m_pipeBranchesWellResultPoints ) { for ( const RigWellResultPoint& wrp : branch ) { isConsistent = isFlowRateConsistent( wrp.flowRate() ); if ( !isConsistent ) break; } if ( !isConsistent ) break; } return isConsistent; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RigAccWellFlowCalculator::calculateAccumulatedFractions( const std::vector& accumulatedFlowPrTracer ) const { double totalFlow = 0.0; for ( double tracerFlow : accumulatedFlowPrTracer ) { totalFlow += tracerFlow; } std::vector 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& branchCells = m_pipeBranchesWellResultPoints[branchIdx]; std::vector resPointUniqueIndexFromBottom = wrpToUniqueWrpIndexFromBottom( branchCells ); size_t prevConnIndx = -1; int clSegIdx = static_cast( branchCells.size() ) - 1; std::vector 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 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( 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 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 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& branchCells = m_pipeBranchesWellResultPoints[branchIdx]; const std::vector& branchClPoints = m_pipeBranchesCLCoords[branchIdx]; const std::vector& branchMDs = m_pipeBranchesMeasuredDepths[branchIdx]; int clSegIdx = static_cast( branchCells.size() ) - 1; std::vector 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 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 flowPrTracer; if ( !isConnectionFlowConsistent( wellCell ) ) { // Associate all the flow with the reservoir tracer for inconsistent flow direction flowPrTracer = std::vector( 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 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 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* accFlowPrTracer, const std::vector& 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 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& accFlowPrTracer, const std::vector& 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& accFlowPrTracer, const std::vector& 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 RigAccWellFlowCalculator::accumulatedDsBranchFlowPrTracer( const BranchFlow& downStreamBranchFlow ) const { std::vector 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 RigAccWellFlowCalculator::calculateWellCellFlowPrTracer( const RigWellResultPoint& wellCell, const std::vector& currentAccumulatedFlowPrTracer ) const { std::vector 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* 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 RigAccWellFlowCalculator::wrpToUniqueWrpIndexFromBottom( const std::vector& branchCells ) const { std::vector resPointToConnectionIndexFromBottom; resPointToConnectionIndexFromBottom.resize( branchCells.size(), -1 ); size_t connIdxFromBottom = 0; int clSegIdx = static_cast( 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& resPointToConnectionIndexFromBottom, size_t clSegIdx ) { return resPointToConnectionIndexFromBottom.front() - resPointToConnectionIndexFromBottom[clSegIdx]; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RigAccWellFlowCalculator::findDownStreamBranchIdxs( const RigWellResultPoint& connectionPoint ) const { std::vector 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 sortedTracers; for ( const QString& tracerName : m_tracerNames ) { const std::vector& 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 tracersToGroup; { bool hasConsistentWellFlow = isWellFlowConsistent(); std::vector> 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 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>* branchFlowSet, const std::vector& tracersToGroup ) { if ( branchFlowSet->empty() ) return; size_t depthCount = branchFlowSet->begin()->second.size(); std::vector groupedAccFlowValues( depthCount, 0.0 ); for ( const QString& tracername : tracersToGroup ) { auto it = branchFlowSet->find( tracername ); if ( it != branchFlowSet->end() ) { const std::vector& 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; }