256 lines
11 KiB
Matlab
256 lines
11 KiB
Matlab
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');
|
|
|
|
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;
|
|
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, numSlices, fluidGradient, rodDepth, ...
|
|
anchorDepth, tubingCSA, pumpArea, frictionEstimate, ...
|
|
structuralRating, kFactor, waterBBLRatio, oilBBLRatio, ...
|
|
gasMCFRatio)
|
|
obj.calculateSPM();
|
|
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.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)
|
|
dhDistanceTop = tDistance;
|
|
obj.topCorner = LPPair(dhPosTarget, dhLoadAtTargetTop);
|
|
end
|
|
|
|
if (bDistance > dhDistanceBottom)
|
|
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 / (now - 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
|