Able to run simulations of cards

This commit is contained in:
Patrick McDonagh
2017-07-27 20:51:22 -05:00
parent 59909df50e
commit df1d2ec893
6 changed files with 284 additions and 48 deletions

View File

@@ -13,6 +13,7 @@ classdef AnalogInput < handle
properties(Access=private) properties(Access=private)
m,b; m,b;
histi;
end end
methods methods
@@ -23,17 +24,23 @@ classdef AnalogInput < handle
obj.rawMax = double(rawMax); obj.rawMax = double(rawMax);
obj.euMin = double(euMin); obj.euMin = double(euMin);
obj.euMax = double(euMax); obj.euMax = double(euMax);
obj.histi = 0;
obj.m = (obj.euMax - obj.euMin) / (obj.rawMax - obj.rawMin); obj.m = (obj.euMax - obj.euMin) / (obj.rawMax - obj.rawMin);
obj.b = obj.euMax - obj.m * (obj.rawMax); obj.b = obj.euMax - obj.m * (obj.rawMax);
end end
function value = setValue(obj, inValue) function value = setValue(obj, inValue)
obj.histi = obj.histi + 1;
obj.rawValue = inValue; obj.rawValue = inValue;
value = obj.m * inValue + obj.b; value = obj.m * inValue + obj.b;
obj.lastValue = value; obj.lastValue = value;
obj.lastStored = now; obj.lastStored = now;
obj.history = [value, obj.history(1:end-1)];
% Store value in history array
histTemp = obj.history(1:end-1);
obj.history(2:end) = histTemp;
obj.history(1) = value;
end end
function value = read(obj) function value = read(obj)
@@ -47,15 +54,12 @@ classdef AnalogInput < handle
end end
if (obj.badReads > 10) if (obj.badReads > 10)
"Bad Reads"
obj.badReads
pause(0.010); pause(0.010);
end end
end end
function value = readSim(obj, simRaw) function value = readSim(obj, simRaw)
value = obj.m * simRaw + obj.b; value = obj.m * simRaw + obj.b;
obj.lastValue = pv;
obj.lastValue = value; obj.lastValue = value;
obj.lastStored = now; obj.lastStored = now;
obj.history = [value, obj.history(1:end-1)]; obj.history = [value, obj.history(1:end-1)];

50
AnalogInputSim.m Normal file
View File

@@ -0,0 +1,50 @@
classdef AnalogInputSim < handle
properties
mux;
channel;
rawValue;
lastValue;
lastStored=0;
rawMax,rawMin,euMax,euMin;
history=zeros(1, 100, 'double');
badReads=0;
end
properties(Access=private)
m,b;
end
methods
function obj = AnalogInputSim(mux, channel, rawMin, rawMax, euMin, euMax)
obj.mux = mux;
obj.channel = channel;
obj.rawMin = double(rawMin);
obj.rawMax = double(rawMax);
obj.euMin = double(euMin);
obj.euMax = double(euMax);
obj.m = (obj.euMax - obj.euMin) / (obj.rawMax - obj.rawMin);
obj.b = obj.euMax - obj.m * (obj.rawMax);
end
function value = setValue(obj, inValue)
obj.rawValue = inValue;
value = obj.m * inValue + obj.b;
obj.lastValue = value;
obj.lastStored = now;
obj.history = [value, obj.history(1:end-1)];
end
function value = read(obj, simRaw)
value = obj.m * simRaw + obj.b;
obj.lastValue = value;
obj.lastStored = now;
% Store value in history array
histTemp = obj.history(1:end-1);
obj.history(2:end) = histTemp;
obj.history(1) = value;
end
end
end

55
Card.m
View File

@@ -65,7 +65,7 @@ classdef Card < handle
anchorDepth, tubingCSA, pumpArea, frictionEstimate, ... anchorDepth, tubingCSA, pumpArea, frictionEstimate, ...
structuralRating, kFactor, waterBBLRatio, oilBBLRatio, ... structuralRating, kFactor, waterBBLRatio, oilBBLRatio, ...
gasMCFRatio) gasMCFRatio)
calculateSPM(); obj.calculateSPM();
obj.surfacePositionMax = obj.positionMax(obj.surfacePosition, obj.surfaceLoad, obj.numPointsUsed); obj.surfacePositionMax = obj.positionMax(obj.surfacePosition, obj.surfaceLoad, obj.numPointsUsed);
obj.surfaceLoadMax = obj.loadMax(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.surfacePositionMin = obj.positionMin(obj.surfacePosition, obj.surfaceLoad, obj.numPointsUsed);
@@ -96,21 +96,21 @@ classdef Card < handle
dhLoadAtTargetTop = 0.0; dhLoadAtTargetTop = 0.0;
dhLoadAtTargetBottom = 0.0; dhLoadAtTargetBottom = 0.0;
for j = 1:obj.numPointsUsed for j = 1:obj.numPointsUsed-1
if (obj.downholePosition(j) <= dhPosTarget && obj.downholePosition(j+1) > dhPosTarget) if (obj.downholePosition(j) <= dhPosTarget && obj.downholePosition(j+1) > dhPosTarget)
dhLoadAtTargetTop = lineResolve(obj.downholePosition(j), obj.downholePosition(j+1), obj.downholeLoad(j), obj.downholeLoad(j+1), dhPosTarget); dhLoadAtTargetTop = obj.lineResolve(obj.downholePosition(j), obj.downholePosition(j+1), obj.downholeLoad(j), obj.downholeLoad(j+1), dhPosTarget);
end end
if (obj.downholePosition(j) > dhPosTarget && obj.downholePosition(j+1) >= dhPosTarget) if (obj.downholePosition(j) > dhPosTarget && obj.downholePosition(j+1) >= dhPosTarget)
dhLoadAtTargetBottom = lineResolve(obj.downholePosition(j), obj.downholePosition(j+1), obj.downholeLoad(j), obj.downholeLoad(j+1), dhPosTarget); dhLoadAtTargetBottom = obj.lineResolve(obj.downholePosition(j), obj.downholePosition(j+1), obj.downholeLoad(j), obj.downholeLoad(j+1), dhPosTarget);
end end
if (obj.surfacePosition(j) <= suPosTarget && obj.surfacePosition(j+1) > suPosTarget) if (obj.surfacePosition(j) <= suPosTarget && obj.surfacePosition(j+1) > suPosTarget)
suLoadAtTargetTop = lineResolve(obj.surfacePosition(j), obj.surfacePosition(j+1), obj.surfaceLoad(j), obj.surfaceLoad(j+1), suPosTarget); suLoadAtTargetTop = obj.lineResolve(obj.surfacePosition(j), obj.surfacePosition(j+1), obj.surfaceLoad(j), obj.surfaceLoad(j+1), suPosTarget);
end end
if (obj.surfacePosition(j) > suPosTarget && obj.surfacePosition(j+1) >= suPosTarget) if (obj.surfacePosition(j) > suPosTarget && obj.surfacePosition(j+1) >= suPosTarget)
suLoadAtTargetBottom = lineResolve(obj.surfacePosition(j), obj.surfacePosition(j+1), obj.surfaceLoad(j), obj.surfaceLoad(j+1), suPosTarget); suLoadAtTargetBottom = obj.lineResolve(obj.surfacePosition(j), obj.surfacePosition(j+1), obj.surfaceLoad(j), obj.surfaceLoad(j+1), suPosTarget);
end end
end end
@@ -135,11 +135,11 @@ classdef Card < handle
obj.downholeNetStrokeLength = obj.bottomCorner.position - obj.downholePositionMin.position; obj.downholeNetStrokeLength = obj.bottomCorner.position - obj.downholePositionMin.position;
obj.fillageCalculated = (obj.downholeNetStrokeLength / obj.downholeAdjustedGrossStrokeLength) * 100.0; obj.fillageCalculated = (obj.downholeNetStrokeLength / obj.downholeAdjustedGrossStrokeLength) * 100.0;
obj.fillageEstimated =(obj.downholeNetStrokeLength / obj.downholeGrossStrokeLength) * 100.0; obj.fillageEstimated =(obj.downholeNetStrokeLength / obj.downholeGrossStrokeLength) * 100.0;
obj.fluidBBLMoved = obj.downholeNetStrokeLength * pumpArea * 0.00010307; obj.fluidBblMoved = obj.downholeNetStrokeLength * pumpArea * 0.00010307;
obj.fluidBBLMovedAdjusted = obj.fluidBBLMoved * kFactor; obj.fluidBblMovedAdjusted = obj.fluidBblMoved * kFactor;
obj.oilBBLMoved = obj.fluidBBLMoved * oilBBLRatio; obj.oilBblMoved = obj.fluidBblMoved * oilBBLRatio;
obj.waterBBLMoved = obj.fluidBBLMoved * waterBBLRatio; obj.waterBblMoved = obj.fluidBblMoved * waterBBLRatio;
obj.gasMCFMoved = obj.fluidBBLMoved * gasMCFRatio; obj.gasMcfMoved = obj.fluidBblMoved * gasMCFRatio;
if (obj.fillageEstimated > 100) if (obj.fillageEstimated > 100)
@@ -169,7 +169,7 @@ classdef Card < handle
obj.strokeSpeed = 60000.0 / (now - obj.strokeStartTime); obj.strokeSpeed = 60000.0 / (now - obj.strokeStartTime);
end end
function dist = distanceToLine(pos, load) function dist = distanceToLine(obj, pos, load)
% Finds the distance between a point and a line between the % Finds the distance between a point and a line between the
% Max Position at Max Load and Min Position at Min Load % Max Position at Max Load and Min Position at Min Load
x1 = obj.downholePositionMin.position; x1 = obj.downholePositionMin.position;
@@ -180,6 +180,27 @@ classdef Card < handle
dist = abs((y2-y1)* pos - (x2-x1)* load + x2*y1 - y2*x1) / sqrt(power(y2-y1, 2) + power(x2-x1,2)); dist = abs((y2-y1)* pos - (x2-x1)* load + x2*y1 - y2*x1) / sqrt(power(y2-y1, 2) + power(x2-x1,2));
end 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) function foundPoint = positionMax(positionArr, loadArr, arrSize)
maxPos = positionArr(1); maxPos = positionArr(1);
loadAtMaxP = -inf; loadAtMaxP = -inf;
@@ -228,16 +249,6 @@ classdef Card < handle
end end
foundPoint = LPPair(posAtMinL, minLoad); foundPoint = LPPair(posAtMinL, minLoad);
end 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
end end

21
MuxSetupSim.m Normal file
View File

@@ -0,0 +1,21 @@
classdef MuxSetupSim < handle
properties(Access=private)
mux1Pin=5;
mux2Pin=6;
mux3Pin=13;
digInPin=19;
anOutTriggerPin=23;
dev;
end
properties
setup=[0 0 0; 1 0 0; 0 1 0; 1 1 0; 0 0 1; 1 0 1; 0 1 1; 1 1 1];
spiDevice;
end
methods
function obj = MuxSetupSim(dev)
obj.dev = dev;
end
end
end

176
Well.m
View File

@@ -3,27 +3,49 @@ classdef Well < handle
% Constants (SHOULD THESE BE ELSEWHERE?) % Constants (SHOULD THESE BE ELSEWHERE?)
YM_STEEL=30.5; YM_STEEL=30.5;
YM_FIBERGLASS=7.2; YM_FIBERGLASS=7.2;
DIRECTION_UNKNOWN=0;
DIRECTION_UP=1;
DIRECTION_DOWN=2;
end end
properties properties
wellName; wellName;
% Instrumentation
device;
mux;
positionSensor;
loadSensor;
% Current Values % Current Values
currentSurfacePosition,currentSurfaceLoad; currentSurfacePosition,currentSurfaceLoad;
currentDownholePosition,currentDownholeLoad; currentDownholePosition,currentDownholeLoad;
currentCard=Card(0);
currentCard=Card(0); lastCard=Card(0);
direction=0;
lastDirection=0;
% User Inputs % User Inputs
dt = 0.03333333; dt = 1.0 / 30.0;
tubingHeadPressure = 40.0; tubingHeadPressure = 40.0;
fluidGradient = 0.45; fluidGradient = 0.45;
stuffingBoxFriction = 100.0; stuffingBoxFriction = 100.0;
numTapers = 3; numTapers = 3;
tubingAnchorDepth = 0.0; tubingAnchorDepth = 0.0;
pumpDiameter = 2.5; pumpDiameter = 2.5;
pumpArea;
tubingInnerDiameter = 1.995; tubingInnerDiameter = 1.995;
tubingOuterDiameter = 2.375; tubingOuterDiameter = 2.375;
tubingCrossSectionalArea;
structuralRating = 320000; structuralRating = 320000;
frictionEstimate;
% Production Parameters
kFactor=1.0;
waterBblRatio=0.90;
oilBblRatio=0.10;
gasMcfRatio=5.0;
% Rod String Inputs % Rod String Inputs
c; c;
@@ -31,7 +53,6 @@ classdef Well < handle
rodDiameter; rodDiameter;
rodYM; rodYM;
rodWeightPerFoot; rodWeightPerFoot;
% Calculated Taper Parameters % Calculated Taper Parameters
a; a;
@@ -47,16 +68,17 @@ classdef Well < handle
centerPoint; centerPoint;
count; count;
topPosArray;
topLoadArray;
buoyantForceTotal = 0.0; buoyantForceTotal = 0.0;
rodDepthTotal = 0.0; rodDepthTotal = 0.0;
rodWeightAirTotal = 0.0; rodWeightAirTotal = 0.0;
rodWeightFluidTotal = 0.0; rodWeightFluidTotal = 0.0;
end
properties(Access=private)
topPosArray;
topLoadArray;
% cross-function variables for position and load % cross-function variables for position and load
% POSITION MUST ALWAYS BE CALLED FIRST. % POSITION MUST ALWAYS BE CALLED FIRST.
loadBefore = 0.0; loadBefore = 0.0;
@@ -64,13 +86,10 @@ classdef Well < handle
loadBefore3 = 0.0; loadBefore3 = 0.0;
loadAfter3 = 0.0; loadAfter3 = 0.0;
tempLoadValue = 0.0; tempLoadValue = 0.0;
end end
methods methods
function me = Well(name) function me = Well(name, sim)
me.wellName = name; me.wellName = name;
me.c = [0.8 0.8 0.8]; me.c = [0.8 0.8 0.8];
@@ -97,10 +116,27 @@ classdef Well < handle
me.topPosArray = zeros(10, 250,'double'); me.topPosArray = zeros(10, 250,'double');
me.topLoadArray = zeros(10, 250, 'double'); me.topLoadArray = zeros(10, 250, 'double');
me.taperSetup(); if logical(sim)
me.device = 0;
me.mux = MuxSetupSim(me.device);
me.positionSensor = AnalogInputSim(me.mux, 1, 0.0, 65535.0, 0.0, 65535.0);
me.loadSensor = AnalogInputSim(me.mux, 2, 0.0, 65535.0, 0.0, 65535.0);
else
me.device = raspi('10.0.0.106', 'pi', 'HenryPump@1903');
me.mux = MuxSetupSim(me.device);
me.positionSensor = AnalogInput(mux, 1, 0.0, 65535.0, 0.0, 140.0);
me.loadSensor = AnalogInput(mux, 2, 0.0, 65535.0, 0.0, 50000.0);
end
me.wellSetup();
end end
function taperSetup(me) function wellSetup(me)
me.tubingCrossSectionalArea = (pi / 4) * ...
(power(me.tubingOuterDiameter, 2) - ...
power(me.tubingInnerDiameter,2));
me.pumpArea = power(me.pumpDiameter, 2) * pi;
for i = 1:me.numTapers for i = 1:me.numTapers
me.area(i) = pi / 4 * power(me.rodDiameter(i), 2); me.area(i) = pi / 4 * power(me.rodDiameter(i), 2);
end end
@@ -153,6 +189,8 @@ classdef Well < handle
me.centerPoint(i) = me.lagIndex(i) + 2; me.centerPoint(i) = me.lagIndex(i) + 2;
me.lengthRequired(i) = 2 * (me.lagIndex(i) + 1) + 1; me.lengthRequired(i) = 2 * (me.lagIndex(i) + 1) + 1;
end end
me.frictionEstimate = me.rodDepthTotal * 0.10;
end end
function pumpPosition = position(me, i) function pumpPosition = position(me, i)
@@ -174,7 +212,7 @@ classdef Well < handle
me.loadAfter3 = 0.0; me.loadAfter3 = 0.0;
pumpPosition = exp(ci * lengthi / (2.0 * ai)) * (me.topPosArray(i, iAfter) + ... pumpPosition = exp(ci * lengthi / (2.0 * ai)) * (me.topPosArray(i, iAfter) + ...
factori * me.topPosArray(i, iAfter+1)); factori * (me.topPosArray(i, iAfter+1) - me.topPosArray(i, iAfter)));
pumpPosition = pumpPosition + exp(-ci * lengthi / (2.0 * ai)) * ... pumpPosition = pumpPosition + exp(-ci * lengthi / (2.0 * ai)) * ...
(me.topPosArray(i,iBefore) + factori * (me.topPosArray(i,iBefore-1) - ... (me.topPosArray(i,iBefore) + factori * (me.topPosArray(i,iBefore-1) - ...
@@ -271,6 +309,8 @@ classdef Well < handle
function [pumpPosition, pumpLoad, status] = calc(me, polishedRodPosition, lastPolishedRodPosition,... function [pumpPosition, pumpLoad, status] = calc(me, polishedRodPosition, lastPolishedRodPosition,...
polishedRodLoad) polishedRodLoad)
me.currentSurfacePosition = polishedRodPosition;
me.currentSurfaceLoad = polishedRodLoad;
pumpPosition = -1.0; pumpPosition = -1.0;
pumpLoad = -1.0; pumpLoad = -1.0;
status = -1; status = -1;
@@ -312,7 +352,9 @@ classdef Well < handle
me.topLoadArray(tap+1, me.lengthRequired(tap+1)) = pumpLoad; me.topLoadArray(tap+1, me.lengthRequired(tap+1)) = pumpLoad;
else else
pumpPosition = -12.0 * me.position(tap); % + me.stretch(me.numTapers); pumpPosition = -12.0 * me.position(tap); % + me.stretch(me.numTapers);
me.currentDownholePosition = pumpPosition;
pumpLoad = me.load(tap) + me.force(me.numTapers); pumpLoad = me.load(tap) + me.force(me.numTapers);
me.currentDownholeLoad = pumpLoad;
status = 1; status = 1;
end end
me.count(tap) = me.count(tap) - 1; me.count(tap) = me.count(tap) - 1;
@@ -324,6 +366,108 @@ classdef Well < handle
tap = tap + 1; tap = tap + 1;
end end
end end
function directionChanged = checkEndOfStroke(me, numConsecutivePoints)
directionChanged = 0;
tempDirection = me.DIRECTION_UNKNOWN;
startDirection = me.DIRECTION_UNKNOWN;
consecutivePointsAllSameDirection = 1;
if me.positionSensor.history(1) > me.positionSensor.history(2)
tempDirection = me.DIRECTION_UP;
startDirection = me.DIRECTION_UP;
elseif me.positionSensor.history(1) < me.positionSensor.history(2)
tempDirection = me.DIRECTION_DOWN;
startDirection = me.DIRECTION_DOWN;
end
if startDirection == me.DIRECTION_UP
for i = 1:numConsecutivePoints
if me.positionSensor.history(i) <= me.positionSensor.history(i+1)
consecutivePointsAllSameDirection = 0;
end
end
if consecutivePointsAllSameDirection == 1
tempDirection = me.DIRECTION_UP;
end
elseif startDirection == me.DIRECTION_DOWN
for i = 1:numConsecutivePoints
if me.positionSensor.history(i) >= me.positionSensor.history(i+1)
consecutivePointsAllSameDirection = 0;
end
end
if consecutivePointsAllSameDirection == 1
tempDirection = me.DIRECTION_DOWN;
end
end
if tempDirection ~= me.lastDirection
if (tempDirection == me.DIRECTION_UP) && ...
(me.currentCard.numPointsUsed > 1)
directionChanged = 1;
end
me.lastDirection = tempDirection;
end
end
function evalSim(me, s_pos, s_load)
me.positionSensor.read(s_pos);
me.loadSensor.read(s_load);
[d_pos, d_load, d_status] = me.calc(me.positionSensor.lastValue, ...
me.positionSensor.history(2), me.loadSensor.lastValue);
if d_status == 1
me.currentCard.push(me.positionSensor.lastValue, ...
me.loadSensor.lastValue, d_pos, d_load);
end
if me.checkEndOfStroke(5) == 1
me.endOfStroke()
end
end
function endOfStroke(me)
me.currentCard.calcStrokeData(100, me.fluidGradient, me.rodDepthTotal, ...
me.tubingAnchorDepth, me.tubingCrossSectionalArea, ...
me.pumpArea, me.frictionEstimate, ...
me.structuralRating, me.kFactor, me.waterBblRatio, me.oilBblRatio, ...
me.gasMcfRatio);
me.plotCards()
me.lastCard = me.currentCard();
me.currentCard = Card(me.lastCard.strokeNumber + 1);
end
function plotCards(me)
ax1 = subplot(2,2,1);
plot(me.lastCard.surfacePosition(1:me.lastCard.numPointsUsed), ...
me.lastCard.surfaceLoad(1:me.lastCard.numPointsUsed))
title('Last Surface Card')
ax3 = subplot(2,2,3);
plot(me.lastCard.downholePosition(1:me.lastCard.numPointsUsed), ...
me.lastCard.downholeLoad(1:me.lastCard.numPointsUsed), 'r')
title('Last Downhole Card')
ax2 = subplot(2,2,2);
plot(me.currentCard.surfacePosition(1:me.currentCard.numPointsUsed), ...
me.currentCard.surfaceLoad(1:me.currentCard.numPointsUsed))
title('Current Surface Card')
ax4 = subplot(2,2,4);
plot(me.currentCard.downholePosition(1:me.currentCard.numPointsUsed), ...
me.currentCard.downholeLoad(1:me.currentCard.numPointsUsed), 'r')
title('Current Downhole Card')
linkaxes([ax1, ax2, ax3, ax4], 'x')
end
end end
methods(Static) methods(Static)

18
test.m
View File

@@ -1,10 +1,16 @@
malletData = csvread('mallet.csv'); malletData = csvread('mallet.csv');
mallet = Well("Mallet"); mallet = Well("Mallet", true(1));
for i = 2:size(malletData,1) figure;
[dhPos, dhLoad, status] = mallet.calc(malletData(i,1), malletData(i-1,1), malletData(i,2)); for loops = 1:6
if status == 1 for i = 2:size(malletData,1)
mallet.currentCard.push(malletData(i,1), malletData(i,2), dhPos, dhLoad); mallet.evalSim(malletData(i,1), malletData(i,2));
end end
end end
figure;plot(mallet.currentCard.downholePosition,mallet.currentCard.downholeLoad)
% mallet.currentCard.calcStrokeData(100, mallet.fluidGradient, mallet.rodDepthTotal, ...
% mallet.tubingAnchorDepth, mallet.tubingCrossSectionalArea, ...
% mallet.pumpArea, mallet.frictionEstimate, ...
% mallet.structuralRating, mallet.kFactor, mallet.waterBblRatio, mallet.oilBblRatio, ...
% mallet.gasMcfRatio)