///////////////////////////////////////////////////////////////////////////////// // // 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 "RigSimWellData.h" #include "RigMainGrid.h" #include "RigActiveCellInfo.h" #include "RigFlowDiagResults.h" #include "RigSimulationWellCoordsAndMD.h" //================================================================================================== /// /// //================================================================================================== //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t RigEclCellIndexCalculator::resultCellIndex(size_t gridIndex, size_t gridCellIndex) const { const RigGridBase* grid = m_mainGrid->gridByIndex(gridIndex); size_t reservoirCellIndex = grid->reservoirCellIndex(gridCellIndex); return m_activeCellInfo->cellResultIndex(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 >& pipeBranchesCLCoords, const std::vector< 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) { 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(RIG_RESERVOIR_TRACER_NAME); initializePipeBranchesMeasuredDepths(); calculateAccumulatedFlowPrConnection(0, 1); calculateFlowPrPseudoLength(0, 0.0); sortTracers(); groupSmallContributions(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigAccWellFlowCalculator::RigAccWellFlowCalculator(const std::vector< std::vector >& pipeBranchesCLCoords, const std::vector< std::vector >& pipeBranchesWellResultPoints, double smallContribThreshold) : m_pipeBranchesCLCoords(pipeBranchesCLCoords), m_pipeBranchesWellResultPoints(pipeBranchesWellResultPoints), m_tracerCellFractionValues(nullptr), m_cellIndexCalculator(RigEclCellIndexCalculator(nullptr, nullptr)), m_smallContributionsThreshold(smallContribThreshold), m_isProducer(true), m_useTotalWellPhaseRateOnly(false) { 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(); calculateAccumulatedFlowPrConnection(0, 1); calculateFlowPrPseudoLength(0, 0.0); 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)), m_smallContributionsThreshold(0.0), m_isProducer(true), m_useTotalWellPhaseRateOnly(totalFlowOnly) { 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(); calculateAccumulatedFlowPrConnection(0, 1); calculateFlowPrPseudoLength(0, 0.0); 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::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 ( !isConnectionFlowConsistent(wellCell) ) { double flowRate = wellCell.flowRate(); flowPrTracer = calculateAccumulatedFractions(currentAccumulatedFlowPrTracer); for (double & accFraction: flowPrTracer) { accFraction *= flowRate; } return flowPrTracer; } if ( m_tracerCellFractionValues ) { if ( wellCell.isCell() && wellCell.m_isOpen ) { size_t resCellIndex = m_cellIndexCalculator.resultCellIndex(wellCell.m_gridIndex, wellCell.m_gridCellIndex); 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].m_gridIndex; size_t prevGridCellIdx = branchCells[clSegIdx].m_gridCellIndex; int prevErtSegId = branchCells[clSegIdx].m_ertSegmentId; int prevErtBranchId = branchCells[clSegIdx].m_ertBranchId; while ( clSegIdx >= 0 ) { if ( branchCells[clSegIdx].isValid() && ( branchCells[clSegIdx].m_gridIndex != prevGridIdx || branchCells[clSegIdx].m_gridCellIndex != prevGridCellIdx || branchCells[clSegIdx].m_ertSegmentId != prevErtSegId || branchCells[clSegIdx].m_ertBranchId != prevErtBranchId) ) { ++connIdxFromBottom; prevGridIdx = branchCells[clSegIdx].m_gridIndex ; prevGridCellIdx = branchCells[clSegIdx].m_gridCellIndex; prevErtSegId = branchCells[clSegIdx].m_ertSegmentId; prevErtBranchId = branchCells[clSegIdx].m_ertBranchId; } 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].m_gridIndex == connectionPoint.m_gridIndex && m_pipeBranchesWellResultPoints[bIdx][0].m_gridCellIndex == connectionPoint.m_gridCellIndex && m_pipeBranchesWellResultPoints[bIdx][0].m_ertBranchId == connectionPoint.m_ertBranchId && m_pipeBranchesWellResultPoints[bIdx][0].m_ertSegmentId == connectionPoint.m_ertSegmentId) { 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(); if ( totalTracerFractions.size() < 5 ) return; // No grouping for few legend items for ( const auto& tracerPair : totalTracerFractions ) { if ( fabs(tracerPair.second) <= m_smallContributionsThreshold && (hasConsistentWellFlow || tracerPair.first != RIG_RESERVOIR_TRACER_NAME) ) // 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; }