classdef Card < handle properties numPointsUsed = 0; strokeNumber; strokeStartTime; surfacePosition=zeros(1, 1500, 'double'); surfaceLoad=zeros(1, 1500, 'double'); downholePosition=zeros(1, 1500, 'double'); downholeLoad=zeros(1, 1500, 'double'); numRepSlices=200; repDownholePosition=zeros(1, 199, 'double'); repDownholeLoadTop=zeros(1, 199, 'double'); repDownholeLoadBottom=zeros(1, 199, 'double'); repSurfacePosition=zeros(1, 199, 'double'); repSurfaceLoadTop=zeros(1, 199, 'double'); repSurfaceLoadBottom=zeros(1, 199, 'double'); surfacePositionMax=LPPair(0,0); surfacePositionMin=LPPair(0,0); surfaceLoadMax=LPPair(0,0); surfaceLoadMin=LPPair(0,0); downholePositionMax=LPPair(0,0); downholePositionMin=LPPair(0,0); downholeLoadMax=LPPair(0,0); downholeLoadMin=LPPair(0,0); topCorner=LPPair(0,0); bottomCorner=LPPair(0,0); surfaceStrokeLength; downholeNetStrokeLength; downholeGrossStrokeLength; downholeAdjustedGrossStrokeLength; downholeLoadSpan; fluidLoad; pumpIntakePressure; fluidLevel; fillageEstimated; fillageCalculated; tubingMovement; strokeSpeed; structuralLoading; polishedRodHorsepower; pumpHorsepower; fluidBblMoved; fluidBblMovedAdjusted; waterBblMoved; oilBblMoved; gasMcfMoved; end methods function obj=Card(strokeNumber) % Initialization Function obj.strokeNumber = strokeNumber; % obj.strokeStartTime = now; obj.strokeStartTime = 0; obj.topCorner = LPPair(0, 0); obj.bottomCorner = LPPair(0, 0); end function push(obj, s_pos, s_load, d_pos, d_load) obj.numPointsUsed = obj.numPointsUsed + 1; obj.surfacePosition(obj.numPointsUsed) = s_pos; obj.surfaceLoad(obj.numPointsUsed) = s_load; obj.downholePosition(obj.numPointsUsed) = d_pos; obj.downholeLoad(obj.numPointsUsed) = d_load; end function calcStrokeData(obj, fluidGradient, rodDepth, ... anchorDepth, tubingCSA, pumpArea, frictionEstimate, ... structuralRating, kFactor, waterBBLRatio, oilBBLRatio, ... gasMCFRatio) obj.calculateSPM(); numSlices = obj.numRepSlices; obj.surfacePositionMax = obj.positionMax(obj.surfacePosition, obj.surfaceLoad, obj.numPointsUsed); obj.surfaceLoadMax = obj.loadMax(obj.surfacePosition, obj.surfaceLoad, obj.numPointsUsed); obj.surfacePositionMin = obj.positionMin(obj.surfacePosition, obj.surfaceLoad, obj.numPointsUsed); obj.surfaceLoadMin = obj.loadMin(obj.surfacePosition, obj.surfaceLoad, obj.numPointsUsed); obj.downholePositionMax = obj.positionMax(obj.downholePosition, obj.downholeLoad, obj.numPointsUsed); obj.downholeLoadMax = obj.loadMax(obj.downholePosition, obj.downholeLoad, obj.numPointsUsed); obj.downholePositionMin = obj.positionMin(obj.downholePosition, obj.downholeLoad, obj.numPointsUsed); obj.downholeLoadMin = obj.loadMin(obj.downholePosition, obj.downholeLoad, obj.numPointsUsed); obj.surfaceStrokeLength = obj.surfacePositionMax.position - obj.surfacePositionMin.position; obj.downholeGrossStrokeLength = obj.downholePositionMax.position - obj.downholePositionMin.position; obj.downholeLoadSpan = obj.downholeLoadMax.load - obj.downholeLoadMin.load; dxSurf = (obj.surfacePositionMax.position - obj.surfacePositionMin.position) / double(numSlices); dxDown = (obj.downholePositionMax.position - obj.downholePositionMin.position) / double(numSlices); obj.pumpHorsepower = 0.0; obj.polishedRodHorsepower = 0.0; dhDistanceTop = 0.0; dhDistanceBottom = 0.0; for i = 1:numSlices-1 suPosTarget = obj.surfacePositionMin.position + (double(i) * dxSurf); dhPosTarget = obj.downholePositionMin.position + (double(i) * dxDown); suLoadAtTargetTop = 0.0; suLoadAtTargetBottom = 0.0; dhLoadAtTargetTop = 0.0; dhLoadAtTargetBottom = 0.0; for j = 1:obj.numPointsUsed-1 if (obj.downholePosition(j) <= dhPosTarget && obj.downholePosition(j+1) > dhPosTarget) dhLoadAtTargetTop = obj.lineResolve(obj.downholePosition(j), obj.downholePosition(j+1), obj.downholeLoad(j), obj.downholeLoad(j+1), dhPosTarget); end if (obj.downholePosition(j) >= dhPosTarget && obj.downholePosition(j+1) < dhPosTarget) dhLoadAtTargetBottom = obj.lineResolve(obj.downholePosition(j), obj.downholePosition(j+1), obj.downholeLoad(j), obj.downholeLoad(j+1), dhPosTarget); end if (obj.surfacePosition(j) <= suPosTarget && obj.surfacePosition(j+1) > suPosTarget) suLoadAtTargetTop = obj.lineResolve(obj.surfacePosition(j), obj.surfacePosition(j+1), obj.surfaceLoad(j), obj.surfaceLoad(j+1), suPosTarget); end if (obj.surfacePosition(j) >= suPosTarget && obj.surfacePosition(j+1) < suPosTarget) suLoadAtTargetBottom = obj.lineResolve(obj.surfacePosition(j), obj.surfacePosition(j+1), obj.surfaceLoad(j), obj.surfaceLoad(j+1), suPosTarget); end end obj.repDownholePosition(i) = dhPosTarget; if (dhLoadAtTargetTop == 0.0) && (i > 1) obj.repDownholeLoadTop(i) = obj.repDownholeLoadTop(i-1); elseif (dhLoadAtTargetTop == 0.0) && (i == 1) obj.repDownholeLoadTop(i) = obj.downholePositionMin.load; else obj.repDownholeLoadTop(i) = dhLoadAtTargetTop; end if dhLoadAtTargetBottom == 0.0 && (i > 1) obj.repDownholeLoadBottom(i) = obj.repDownholeLoadBottom(i-1); elseif dhLoadAtTargetBottom == 0.0 && (i == 1) obj.repDownholeLoadBottom(i) = obj.downholePositionMin.load; else obj.repDownholeLoadBottom(i) = dhLoadAtTargetBottom; end obj.repSurfacePosition(i) = suPosTarget; if suLoadAtTargetTop == 0.0 && (i > 1) obj.repSurfaceLoadTop(i) = obj.repSurfaceLoadTop(i-1); elseif suLoadAtTargetTop == 0.0 && (i == 1) obj.repSurfaceLoadTop(i) = obj.surfacePositionMin.load; else obj.repSurfaceLoadTop(i) = suLoadAtTargetTop; end if suLoadAtTargetBottom == 0.0 && (i > 1) obj.repSurfaceLoadBottom(i) = obj.repSurfaceLoadBottom(i-1); elseif suLoadAtTargetBottom == 0.0 && (i == 1) obj.repSurfaceLoadBottom(i) = obj.surfacePositionMin.load; else obj.repSurfaceLoadBottom(i) = suLoadAtTargetBottom; end obj.polishedRodHorsepower = obj.polishedRodHorsepower + (dxSurf / 12.0) * (suLoadAtTargetTop - suLoadAtTargetBottom) * (obj.strokeSpeed / 33000.0); obj.pumpHorsepower = obj.pumpHorsepower + (dxDown / 12.0) * (dhLoadAtTargetTop - dhLoadAtTargetBottom) * (obj.strokeSpeed / 33000.0); tDistance = obj.distanceToLine(dhPosTarget, dhLoadAtTargetTop); bDistance = obj.distanceToLine(dhPosTarget, dhLoadAtTargetBottom); if (tDistance > dhDistanceTop) && (dhLoadAtTargetTop ~= 0.0) dhDistanceTop = tDistance; obj.topCorner = LPPair(dhPosTarget, dhLoadAtTargetTop); end if (bDistance > dhDistanceBottom) && (dhLoadAtTargetBottom ~= 0.0) dhDistanceBottom = bDistance; obj.bottomCorner = LPPair(dhPosTarget, dhLoadAtTargetBottom); end end obj.downholeAdjustedGrossStrokeLength = obj.downholePositionMax.position - obj.topCorner.position; obj.downholeNetStrokeLength = obj.bottomCorner.position - obj.downholePositionMin.position; obj.fillageCalculated = (obj.downholeNetStrokeLength / obj.downholeAdjustedGrossStrokeLength) * 100.0; obj.fillageEstimated =(obj.downholeNetStrokeLength / obj.downholeGrossStrokeLength) * 100.0; obj.fluidBblMoved = obj.downholeNetStrokeLength * pumpArea * 0.00010307; obj.fluidBblMovedAdjusted = obj.fluidBblMoved * kFactor; obj.oilBblMoved = obj.fluidBblMoved * oilBBLRatio; obj.waterBblMoved = obj.fluidBblMoved * waterBBLRatio; obj.gasMcfMoved = obj.fluidBblMoved * gasMCFRatio; if (obj.fillageEstimated > 100) obj.fillageEstimated = 100.0; end if (obj.fillageEstimated < 0.0) obj.fillageEstimated = 0.0; end if (obj.fillageCalculated > 100) obj.fillageCalculated = 100.0; end if (obj.fillageCalculated < 0.0) obj.fillageCalculated = 0.0; end obj.fluidLoad = obj.downholeLoadSpan - frictionEstimate; obj.pumpIntakePressure = fluidGradient * rodDepth - (obj.fluidLoad / pumpArea); obj.fluidLevel = obj.pumpIntakePressure / fluidGradient; obj.tubingMovement = 12 * (rodDepth - anchorDepth) * obj.fluidLoad / (30500000 * tubingCSA); obj.structuralLoading = (obj.surfaceLoadMax.load / (structuralRating * 100)) * 100; end function calculateSPM(obj) obj.strokeSpeed = 60000.0 / (5000 - obj.strokeStartTime); end function dist = distanceToLine(obj, pos, load) % Finds the distance between a point and a line between the % Max Position at Max Load and Min Position at Min Load x1 = obj.downholePositionMin.position; x2 = obj.downholePositionMax.position; y1 = obj.downholeLoadMin.load; y2 = obj.downholeLoadMax.load; dist = abs((y2-y1)* pos - (x2-x1)* load + x2*y1 - y2*x1) / sqrt(power(y2-y1, 2) + power(x2-x1,2)); end function plot(obj) ax1 = subplot(2,1,1); plot(obj.surfacePosition(1:obj.numPointsUsed), obj.surfaceLoad(1:obj.numPointsUsed)) title('Surface Card') ax2 = subplot(2,1,2); plot(obj.downholePosition(1:obj.numPointsUsed),obj.downholeLoad(1:obj.numPointsUsed), 'r') title('Downhole Card') linkaxes([ax1, ax2], 'x') end end methods(Static) function yTest = lineResolve(x1, x2, y1, y2, xTest) % Uses the standard line equation to find the y value given x line_m = (y2 - y1) / (x2 - x1); line_b = y1 - line_m * x1; yTest = line_m * xTest + line_b; end function foundPoint = positionMax(positionArr, loadArr, arrSize) maxPos = positionArr(1); loadAtMaxP = -inf; for i = 1:arrSize maxPos = max(maxPos, positionArr(i)); if (maxPos == positionArr(i)) loadAtMaxP = loadArr(i); end end foundPoint = LPPair(maxPos, loadAtMaxP); end function foundPoint = positionMin(positionArr, loadArr, arrSize) minPosition = positionArr(1); loadAtMinP = inf; for i = 1:arrSize minPosition = min(minPosition, positionArr(i)); if (minPosition == positionArr(i)) loadAtMinP = loadArr(i); end end foundPoint = LPPair(minPosition, loadAtMinP); end function foundPoint = loadMax(positionArr, loadArr, arrSize) maxLoad = loadArr(1); posAtMaxL = -inf; for i = 1:arrSize maxLoad = max(maxLoad, loadArr(i)); if (maxLoad == positionArr(i)) posAtMaxL = positionArr(i); end end foundPoint = LPPair(posAtMaxL, maxLoad); end function foundPoint = loadMin(positionArr, loadArr, arrSize) minLoad = loadArr(1); posAtMinL = inf; for i = 1:arrSize minLoad = min(minLoad, loadArr(i)); if (minLoad == positionArr(i)) posAtMinL = positionArr(i); end end foundPoint = LPPair(posAtMinL, minLoad); end end end