1110 lines
54 KiB
HTML
1110 lines
54 KiB
HTML
|
|
<!DOCTYPE html
|
|
PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
|
<html><head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
<!--
|
|
This HTML was auto-generated from MATLAB code.
|
|
To make changes, update the MATLAB code and republish this document.
|
|
--><title>Well</title><meta name="generator" content="MATLAB 9.2"><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/"><meta name="DC.date" content="2017-07-31"><meta name="DC.source" content="Well.m"><style type="text/css">
|
|
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}:focus{outine:0}ins{text-decoration:none}del{text-decoration:line-through}table{border-collapse:collapse;border-spacing:0}
|
|
|
|
html { min-height:100%; margin-bottom:1px; }
|
|
html body { height:100%; margin:0px; font-family:Arial, Helvetica, sans-serif; font-size:10px; color:#000; line-height:140%; background:#fff none; overflow-y:scroll; }
|
|
html body td { vertical-align:top; text-align:left; }
|
|
|
|
h1 { padding:0px; margin:0px 0px 25px; font-family:Arial, Helvetica, sans-serif; font-size:1.5em; color:#d55000; line-height:100%; font-weight:normal; }
|
|
h2 { padding:0px; margin:0px 0px 8px; font-family:Arial, Helvetica, sans-serif; font-size:1.2em; color:#000; font-weight:bold; line-height:140%; border-bottom:1px solid #d6d4d4; display:block; }
|
|
h3 { padding:0px; margin:0px 0px 5px; font-family:Arial, Helvetica, sans-serif; font-size:1.1em; color:#000; font-weight:bold; line-height:140%; }
|
|
|
|
a { color:#005fce; text-decoration:none; }
|
|
a:hover { color:#005fce; text-decoration:underline; }
|
|
a:visited { color:#004aa0; text-decoration:none; }
|
|
|
|
p { padding:0px; margin:0px 0px 20px; }
|
|
img { padding:0px; margin:0px 0px 20px; border:none; }
|
|
p img, pre img, tt img, li img, h1 img, h2 img { margin-bottom:0px; }
|
|
|
|
ul { padding:0px; margin:0px 0px 20px 23px; list-style:square; }
|
|
ul li { padding:0px; margin:0px 0px 7px 0px; }
|
|
ul li ul { padding:5px 0px 0px; margin:0px 0px 7px 23px; }
|
|
ul li ol li { list-style:decimal; }
|
|
ol { padding:0px; margin:0px 0px 20px 0px; list-style:decimal; }
|
|
ol li { padding:0px; margin:0px 0px 7px 23px; list-style-type:decimal; }
|
|
ol li ol { padding:5px 0px 0px; margin:0px 0px 7px 0px; }
|
|
ol li ol li { list-style-type:lower-alpha; }
|
|
ol li ul { padding-top:7px; }
|
|
ol li ul li { list-style:square; }
|
|
|
|
.content { font-size:1.2em; line-height:140%; padding: 20px; }
|
|
|
|
pre, code { font-size:12px; }
|
|
tt { font-size: 1.2em; }
|
|
pre { margin:0px 0px 20px; }
|
|
pre.codeinput { padding:10px; border:1px solid #d3d3d3; background:#f7f7f7; }
|
|
pre.codeoutput { padding:10px 11px; margin:0px 0px 20px; color:#4c4c4c; }
|
|
pre.error { color:red; }
|
|
|
|
@media print { pre.codeinput, pre.codeoutput { word-wrap:break-word; width:100%; } }
|
|
|
|
span.keyword { color:#0000FF }
|
|
span.comment { color:#228B22 }
|
|
span.string { color:#A020F0 }
|
|
span.untermstring { color:#B20000 }
|
|
span.syscmd { color:#B28C00 }
|
|
|
|
.footer { width:auto; padding:10px 0px; margin:25px 0px 0px; border-top:1px dotted #878787; font-size:0.8em; line-height:140%; font-style:italic; color:#878787; text-align:left; float:none; }
|
|
.footer p { margin:0px; }
|
|
.footer a { color:#878787; }
|
|
.footer a:hover { color:#878787; text-decoration:underline; }
|
|
.footer a:visited { color:#878787; }
|
|
|
|
table th { padding:7px 5px; text-align:left; vertical-align:middle; border: 1px solid #d6d4d4; font-weight:bold; }
|
|
table td { padding:7px 5px; text-align:left; vertical-align:top; border:1px solid #d6d4d4; }
|
|
|
|
|
|
|
|
|
|
|
|
</style></head><body><div class="content"><h2>Contents</h2><div><ul><li><a href="#2">Constants (SHOULD THESE BE ELSEWHERE?)</a></li><li><a href="#5">Instrumentation</a></li><li><a href="#6">Current Values</a></li><li><a href="#7">User Inputs</a></li><li><a href="#8">Production Parameters</a></li><li><a href="#9">Rod String Inputs</a></li><li><a href="#10">Calculated Taper Parameters</a></li><li><a href="#12">Constructor Function</a></li><li><a href="#13">Well Setup Function</a></li><li><a href="#14">Position Function (From Algorithm)</a></li><li><a href="#15">Load Function (From Algorithm)</a></li><li><a href="#16">Calc Function</a></li><li><a href="#17">Check if the stroke has ended</a></li><li><a href="#18">Evaluation function for simulated position and load points</a></li><li><a href="#19">End of Stroke actions</a></li><li><a href="#20">Draws Cards</a></li><li><a href="#22">Determines Rod Weight Per Foot given Young's Modulus and Diameter</a></li></ul></div><pre class="codeinput"><span class="keyword">classdef</span> Well < handle
|
|
<span class="keyword">properties</span>(Constant)
|
|
</pre><h2 id="2">Constants (SHOULD THESE BE ELSEWHERE?)</h2><pre class="codeinput"> YM_STEEL=30.5;
|
|
YM_FIBERGLASS=7.2;
|
|
|
|
DIRECTION_UNKNOWN=0;
|
|
DIRECTION_UP=1;
|
|
DIRECTION_DOWN=2;
|
|
</pre><pre class="codeinput"> <span class="keyword">end</span>
|
|
|
|
properties
|
|
</pre><h2 id="5">Instrumentation</h2><pre class="codeinput"> device;
|
|
mux;
|
|
positionSensor;
|
|
loadSensor;
|
|
</pre><h2 id="6">Current Values</h2><pre class="codeinput"> currentSurfacePosition,currentSurfaceLoad;
|
|
currentDownholePosition,currentDownholeLoad;
|
|
currentCard=Card(0);
|
|
lastCard=Card(0);
|
|
direction=0;
|
|
lastDirection=0;
|
|
</pre><h2 id="7">User Inputs</h2><pre class="codeinput"> wellName;
|
|
dt = 1.0 / 30.0;
|
|
tubingHeadPressure = 40.0;
|
|
fluidGradient = 0.45;
|
|
stuffingBoxFriction = 100.0;
|
|
numTapers = 3;
|
|
tubingAnchorDepth = 0.0;
|
|
pumpDiameter = 2.5;
|
|
pumpArea;
|
|
tubingInnerDiameter = 1.995;
|
|
tubingOuterDiameter = 2.375;
|
|
tubingCrossSectionalArea;
|
|
structuralRating = 320000;
|
|
frictionEstimate;
|
|
</pre><h2 id="8">Production Parameters</h2><pre class="codeinput"> kFactor=1.0;
|
|
waterBblRatio=0.90;
|
|
oilBblRatio=0.10;
|
|
gasMcfRatio=5.0;
|
|
</pre><h2 id="9">Rod String Inputs</h2><pre class="codeinput"> c;
|
|
rodLength;
|
|
rodDiameter;
|
|
rodYM;
|
|
rodWeightPerFoot;
|
|
</pre><h2 id="10">Calculated Taper Parameters</h2><pre class="codeinput"> a;
|
|
area;
|
|
pressure;
|
|
stretch;
|
|
force;
|
|
alpha;
|
|
xOverA;
|
|
factorArray;
|
|
lagIndex;
|
|
lengthRequired;
|
|
centerPoint;
|
|
count;
|
|
|
|
buoyantForceTotal = 0.0;
|
|
rodDepthTotal = 0.0;
|
|
rodWeightAirTotal = 0.0;
|
|
rodWeightFluidTotal = 0.0;
|
|
</pre><pre class="codeinput"> <span class="keyword">end</span>
|
|
|
|
properties(Access=private)
|
|
topPosArray;
|
|
topLoadArray;
|
|
|
|
<span class="comment">% cross-function variables for position and load</span>
|
|
<span class="comment">% POSITION MUST ALWAYS BE CALLED FIRST.</span>
|
|
loadBefore = 0.0;
|
|
loadAfter = 0.0;
|
|
loadBefore3 = 0.0;
|
|
loadAfter3 = 0.0;
|
|
tempLoadValue = 0.0;
|
|
<span class="keyword">end</span>
|
|
|
|
methods
|
|
</pre><h2 id="12">Constructor Function</h2><pre class="codeinput"> <span class="keyword">function</span> me = Well(name, sim)
|
|
me.wellName = name;
|
|
|
|
me.c = [0.8 0.8 0.8];
|
|
me.rodLength = [1475.0 1525.0 2000.0];
|
|
me.rodDiameter = [1.0 0.875 0.750];
|
|
me.rodYM = [me.YM_STEEL me.YM_STEEL me.YM_STEEL];
|
|
me.rodWeightPerFoot = [Well.lookupRodWeightPerFoot(me.rodYM(1), me.rodDiameter(1)) <span class="keyword">...</span>
|
|
Well.lookupRodWeightPerFoot(me.rodYM(2), me.rodDiameter(2)) <span class="keyword">...</span>
|
|
Well.lookupRodWeightPerFoot(me.rodYM(3), me.rodDiameter(3))];
|
|
|
|
<span class="comment">% Initialize</span>
|
|
me.a = zeros(1, me.numTapers, <span class="string">'double'</span>);
|
|
me.area = zeros(1, me.numTapers+1, <span class="string">'double'</span>);
|
|
me.pressure = zeros(1, me.numTapers, <span class="string">'double'</span>);
|
|
me.stretch = zeros(1, me.numTapers, <span class="string">'double'</span>);
|
|
me.force = zeros(1, me.numTapers, <span class="string">'double'</span>);
|
|
me.alpha = zeros(1, me.numTapers, <span class="string">'double'</span>);
|
|
me.xOverA = zeros(1, me.numTapers, <span class="string">'double'</span>);
|
|
me.factorArray = zeros(1, me.numTapers, <span class="string">'double'</span>);
|
|
me.lagIndex = zeros(1, me.numTapers, <span class="string">'uint8'</span>);
|
|
me.lengthRequired = zeros(1, me.numTapers, <span class="string">'uint8'</span>);
|
|
me.centerPoint = zeros(1, me.numTapers, <span class="string">'uint8'</span>);
|
|
me.count = zeros(1, me.numTapers, <span class="string">'uint8'</span>);
|
|
me.topPosArray = zeros(10, 250,<span class="string">'double'</span>);
|
|
me.topLoadArray = zeros(10, 250, <span class="string">'double'</span>);
|
|
|
|
<span class="keyword">if</span> 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);
|
|
<span class="keyword">else</span>
|
|
me.device = raspi(<span class="string">'10.0.0.106'</span>, <span class="string">'pi'</span>, <span class="string">'HenryPump@1903'</span>);
|
|
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);
|
|
<span class="keyword">end</span>
|
|
|
|
me.wellSetup();
|
|
<span class="keyword">end</span>
|
|
</pre><pre class="codeoutput error">Not enough input arguments.
|
|
|
|
Error in Well (line 96)
|
|
me.wellName = name;
|
|
</pre><h2 id="13">Well Setup Function</h2><pre class="codeinput"> <span class="keyword">function</span> wellSetup(me)
|
|
me.tubingCrossSectionalArea = (pi / 4) * <span class="keyword">...</span>
|
|
(power(me.tubingOuterDiameter, 2) - <span class="keyword">...</span>
|
|
power(me.tubingInnerDiameter,2));
|
|
me.pumpArea = power(me.pumpDiameter, 2) * pi;
|
|
|
|
<span class="keyword">for</span> i = 1:me.numTapers
|
|
me.area(i) = pi / 4 * power(me.rodDiameter(i), 2);
|
|
<span class="keyword">end</span>
|
|
|
|
<span class="keyword">for</span> i = 1:me.numTapers
|
|
me.a(i) = 1000.0 * sqrt(32.2 * me.rodYM(i) * me.area(i) / me.rodWeightPerFoot(i));
|
|
me.rodDepthTotal = me.rodDepthTotal + me.rodLength(i);
|
|
|
|
<span class="keyword">if</span> i > 1
|
|
me.pressure(i) = me.pressure(i - 1) + me.fluidGradient * me.rodLength(i);
|
|
<span class="keyword">else</span>
|
|
me.pressure(i) = me.tubingHeadPressure + me.fluidGradient * me.rodLength(i);
|
|
<span class="keyword">end</span>
|
|
|
|
me.buoyantForceTotal = me.buoyantForceTotal + me.pressure(i) * (me.area(i+1) - me.area(i));
|
|
me.rodWeightAirTotal = me.rodWeightAirTotal + me.rodWeightPerFoot(i) * me.rodLength(i);
|
|
me.rodWeightFluidTotal = me.rodWeightAirTotal + me.buoyantForceTotal;
|
|
<span class="keyword">end</span>
|
|
|
|
<span class="keyword">for</span> j = 1:me.numTapers
|
|
weightData = 0.0;
|
|
annularForceData = 0.0;
|
|
|
|
<span class="keyword">for</span> i = j+1:me.numTapers
|
|
weightData = weightData + me.rodWeightPerFoot(i) * me.rodLength(i);
|
|
<span class="keyword">end</span>
|
|
|
|
<span class="keyword">for</span> i = j:me.numTapers-1
|
|
annularForceData = annularForceData + me.pressure(i) * (me.area(i) - me.area(i+1));
|
|
<span class="keyword">end</span>
|
|
|
|
me.force(j) = (-me.area(me.numTapers) * me.pressure(me.numTapers)) + weightData - annularForceData;
|
|
me.alpha(j) = (me.force(j) + me.rodWeightPerFoot(j) * me.rodLength(j)) / (me.rodYM(j) * power(10, 6) * me.area(j));
|
|
|
|
<span class="keyword">if</span> j > 1
|
|
me.stretch(j) = me.stretch(j - 1) + me.alpha(j) * me.rodLength(j) - <span class="keyword">...</span>
|
|
(me.rodWeightPerFoot(j) * power(me.rodLength(j), 2.0)) / <span class="keyword">...</span>
|
|
(2.0 * me.rodYM(j) * power(10, 6) * me.area(j));
|
|
<span class="keyword">else</span>
|
|
me.stretch(j) = 0.0 + me.alpha(j) * me.rodLength(j) - <span class="keyword">...</span>
|
|
(me.rodWeightPerFoot(j) * power(me.rodLength(j), 2.0)) / <span class="keyword">...</span>
|
|
(2.0 * me.rodYM(j) * power(10, 6) * me.area(j));
|
|
<span class="keyword">end</span>
|
|
<span class="keyword">end</span>
|
|
|
|
<span class="keyword">for</span> i = 1:me.numTapers
|
|
me.xOverA(i) = me.rodLength(i) / me.a(i);
|
|
me.lagIndex(i) = floor(me.rodLength(i) / (me.a(i) * me.dt));
|
|
me.factorArray(i) = (me.xOverA(i) - double(me.lagIndex(i)) * me.dt) / me.dt;
|
|
me.centerPoint(i) = me.lagIndex(i) + 2;
|
|
me.lengthRequired(i) = 2 * (me.lagIndex(i) + 1) + 1;
|
|
<span class="keyword">end</span>
|
|
|
|
me.frictionEstimate = me.rodDepthTotal * 0.10;
|
|
<span class="keyword">end</span>
|
|
</pre><h2 id="14">Position Function (From Algorithm)</h2><pre class="codeinput"> <span class="keyword">function</span> pumpPosition = position(me, i)
|
|
<span class="comment">% Position Function</span>
|
|
ai = me.a(i);
|
|
ci = me.c(i);
|
|
factori = me.factorArray(i);
|
|
lengthi = me.rodLength(i);
|
|
lagi = double(me.lagIndex(i));
|
|
yi = me.rodYM(i) * power(10, 6);
|
|
areai = me.area(i);
|
|
centeri = me.centerPoint(i);
|
|
iBefore = centeri - me.lagIndex(i);
|
|
iAfter = centeri + me.lagIndex(i);
|
|
|
|
me.loadBefore = 0.0;
|
|
me.loadAfter = 0.0;
|
|
me.loadBefore3 = 0.0;
|
|
me.loadAfter3 = 0.0;
|
|
|
|
pumpPosition = exp(ci * lengthi / (2.0 * ai)) * (me.topPosArray(i, iAfter) + <span class="keyword">...</span>
|
|
factori * (me.topPosArray(i, iAfter+1) - me.topPosArray(i, iAfter)));
|
|
|
|
pumpPosition = pumpPosition + exp(-ci * lengthi / (2.0 * ai)) * <span class="keyword">...</span>
|
|
(me.topPosArray(i,iBefore) + factori * (me.topPosArray(i,iBefore-1) - <span class="keyword">...</span>
|
|
me.topPosArray(i, iBefore)));
|
|
|
|
pumpPosition = 0.5 * pumpPosition;
|
|
insideIntegral = 0.0;
|
|
|
|
<span class="keyword">for</span> jj = 1:2*lagi-1
|
|
insideIntegral = insideIntegral + me.dt / (yi * areai) * <span class="keyword">...</span>
|
|
(exp((-ci * (lagi - jj) *me.dt) / 2.0) * <span class="keyword">...</span>
|
|
me.topLoadArray(i, iBefore + jj));
|
|
<span class="keyword">end</span>
|
|
|
|
insideIntegral = insideIntegral + 0.5 * me.dt /(yi * areai) * <span class="keyword">...</span>
|
|
(exp((-ci * lagi * me.dt) / 2.0) * me.topLoadArray(i, iBefore) + <span class="keyword">...</span>
|
|
exp((-ci * -lagi * me.dt) / 2.0) * me.topLoadArray(i, iAfter));
|
|
|
|
me.loadBefore = exp((-ci * lagi * me.dt) / 2.0) * me.topLoadArray(i, iBefore) + <span class="keyword">...</span>
|
|
factori * (exp((-ci * (lagi + 1) * me.dt) / 2.0) * me.topLoadArray(i, iBefore-1) - <span class="keyword">...</span>
|
|
exp((-ci * lagi * me.dt) / 2.0) * me.topLoadArray(i, iBefore));
|
|
|
|
me.loadAfter = exp((-ci * -lagi * me.dt) / 2.0) * me.topLoadArray(i, iAfter) + <span class="keyword">...</span>
|
|
factori * (exp((-ci * (-lagi - 1) * me.dt) / 2.0) * me.topLoadArray(i, iAfter+1) - <span class="keyword">...</span>
|
|
exp((-ci * -lagi * me.dt) / 2.0) * me.topLoadArray(i,iAfter));
|
|
|
|
insideIntegral = insideIntegral + 0.5 * factori * me.dt / (yi * areai) * <span class="keyword">...</span>
|
|
(me.loadBefore + exp((-ci * lagi * me.dt) / 2.0) * me.topLoadArray(i, iBefore));
|
|
|
|
insideIntegral = insideIntegral + 0.5 * factori * me.dt / (yi * areai) * <span class="keyword">...</span>
|
|
(me.loadAfter + exp((-ci * -lagi * me.dt) / 2.0) * me.topLoadArray(i, iAfter));
|
|
|
|
insideIntegral = 0.5 * ai * insideIntegral;
|
|
pumpPosition = pumpPosition + insideIntegral;
|
|
|
|
insideIntegral = 0.0;
|
|
|
|
<span class="keyword">for</span> jj = 1:2*lagi-1
|
|
insideIntegral = insideIntegral + me.dt * (exp((-ci * (lagi - jj) * me.dt) / 2.0) * <span class="keyword">...</span>
|
|
me.topPosArray(i, iBefore+jj));
|
|
<span class="keyword">end</span>
|
|
|
|
insideIntegral = insideIntegral + 0.5 * me.dt * (exp((-ci * lagi * me.dt) / 2.0) * <span class="keyword">...</span>
|
|
me.topPosArray(i, iBefore) + exp((-ci * -lagi * me.dt) / 2.0) * <span class="keyword">...</span>
|
|
me.topPosArray(i, iAfter));
|
|
|
|
me.loadBefore3 = exp((-ci * lagi * me.dt) / 2.0) * me.topPosArray(i, iBefore) + <span class="keyword">...</span>
|
|
factori * (exp((-ci * (lagi+1) * me.dt) / 2.0) * me.topPosArray(i, iBefore-1) - <span class="keyword">...</span>
|
|
exp((-ci * lagi * me.dt) / 2.0) * me.topPosArray(i, iBefore));
|
|
|
|
me.loadAfter3 = exp((-ci * -lagi * me.dt) / 2.0) * me.topPosArray(i, iAfter) + <span class="keyword">...</span>
|
|
factori * (exp((-ci * (-lagi-1) * me.dt) / 2.0) * me.topPosArray(i, iAfter+1) - <span class="keyword">...</span>
|
|
exp((-ci * -lagi * me.dt) / 2.0) * me.topPosArray(i, iAfter));
|
|
|
|
insideIntegral = insideIntegral + 0.5 * factori * me.dt * (me.loadBefore3 + <span class="keyword">...</span>
|
|
exp((-ci * lagi * me.dt) / 2.0) * me.topPosArray(i, iBefore));
|
|
|
|
insideIntegral = insideIntegral + 0.5 * factori * me.dt * (me.loadAfter3 + <span class="keyword">...</span>
|
|
exp((-ci * -lagi * me.dt) / 2.0) * me.topPosArray(i, iAfter));
|
|
|
|
insideIntegral = -(ci * lengthi / 4) * 0.5 * (ci / (2.0 * ai)) * insideIntegral;
|
|
me.tempLoadValue = insideIntegral / lengthi;
|
|
pumpPosition = pumpPosition + insideIntegral;
|
|
<span class="keyword">end</span>
|
|
</pre><h2 id="15">Load Function (From Algorithm)</h2><pre class="codeinput"> <span class="keyword">function</span> pumpLoad = load(me, i)
|
|
<span class="comment">% Load Function</span>
|
|
ai = me.a(i);
|
|
ci = me.c(i);
|
|
lengthi = me.rodLength(i);
|
|
lagi = me.lagIndex(i);
|
|
yi = me.rodYM(i) * power(10, 6);
|
|
areai = me.area(i);
|
|
centeri = me.centerPoint(i);
|
|
iBefore = centeri - lagi;
|
|
iAfter = centeri + lagi;
|
|
|
|
pumpLoad = 0.5 * (ai / (yi * areai)) * (1 / ai) * (me.loadBefore + me.loadAfter);
|
|
tempResult = yi * areai * pumpLoad;
|
|
pumpLoad = pumpLoad - (ci * lengthi / 4) * (0.5 * (ci / (2.0 * ai))) * (1 / ai) * <span class="keyword">...</span>
|
|
(me.loadBefore3 + me.loadAfter3);
|
|
|
|
ptAfter = (me.topPosArray(i, iAfter+1) - me.topPosArray(i, iAfter-1)) / (2.0 * me.dt);
|
|
ptBefore = (me.topPosArray(i, iBefore+1) - me.topPosArray(i, iBefore-1)) / (2.0 * me.dt);
|
|
firstPart = (exp((ci * lengthi) / (2.0 * ai)) * ptAfter - exp((-ci * lengthi) / <span class="keyword">...</span>
|
|
(2.0 * ai)) * ptBefore) / (2.0 * ai);
|
|
|
|
firstPart = firstPart + (ci * exp((ci * lengthi) / (2.0 * ai)) * <span class="keyword">...</span>
|
|
me.topPosArray(i, iAfter) - ci * exp((-ci * lengthi) / (2.0 * ai)) * <span class="keyword">...</span>
|
|
me.topPosArray(i, iBefore)) / (4 * ai);
|
|
|
|
pumpLoad = yi * areai * (firstPart + pumpLoad); <span class="comment">% + tempLoadValue ?</span>
|
|
<span class="keyword">end</span>
|
|
</pre><h2 id="16">Calc Function</h2><p>Calls position and load functions</p><pre class="codeinput"> <span class="keyword">function</span> [pumpPosition, pumpLoad, status] = calc(me, polishedRodPosition, lastPolishedRodPosition,<span class="keyword">...</span>
|
|
polishedRodLoad)
|
|
me.currentSurfacePosition = polishedRodPosition;
|
|
me.currentSurfaceLoad = polishedRodLoad;
|
|
pumpPosition = -1.0;
|
|
pumpLoad = -1.0;
|
|
status = -1;
|
|
loadMult = 1.0;
|
|
tapersAllowed = 1;
|
|
|
|
<span class="keyword">for</span> ii = 2:me.lengthRequired(1)
|
|
me.topPosArray(1, ii-1) = me.topPosArray(1, ii);
|
|
me.topLoadArray(1, ii-1) = me.topLoadArray(1, ii);
|
|
<span class="keyword">end</span>
|
|
|
|
me.topPosArray(1, me.lengthRequired(1)) = -1.0 * polishedRodPosition / 12.0;
|
|
|
|
<span class="keyword">if</span> polishedRodPosition > lastPolishedRodPosition
|
|
me.topLoadArray(1, me.lengthRequired(1)) = loadMult * <span class="keyword">...</span>
|
|
(polishedRodLoad - me.rodWeightFluidTotal) - me.stuffingBoxFriction;
|
|
<span class="keyword">elseif</span> polishedRodPosition < lastPolishedRodPosition
|
|
me.topLoadArray(1, me.lengthRequired(1)) = loadMult * <span class="keyword">...</span>
|
|
(polishedRodLoad - me.rodWeightFluidTotal) + me.stuffingBoxFriction;
|
|
<span class="keyword">else</span>
|
|
me.topLoadArray(1, me.lengthRequired(1)) = loadMult * <span class="keyword">...</span>
|
|
(polishedRodLoad - me.rodWeightFluidTotal);
|
|
<span class="keyword">end</span>
|
|
|
|
tap=1;
|
|
<span class="keyword">while</span> tap <= tapersAllowed
|
|
me.count(tap) = me.count(tap) + 1;
|
|
<span class="keyword">if</span> me.count(tap) >= me.lengthRequired(tap)
|
|
<span class="keyword">if</span> tap+1 <= me.numTapers
|
|
<span class="comment">% working our way down to the bottom of the well</span>
|
|
<span class="keyword">for</span> pti = 2:me.lengthRequired(tap+1)+1
|
|
me.topPosArray(tap+1, pti-1) = me.topPosArray(tap+1, pti);
|
|
me.topLoadArray(tap+1, pti-1) = me.topLoadArray(tap+1, pti);
|
|
<span class="keyword">end</span>
|
|
pumpPosition = me.position(tap);
|
|
pumpLoad = me.load(tap);
|
|
status = 0;
|
|
me.topPosArray(tap+1, me.lengthRequired(tap+1)) = pumpPosition;
|
|
me.topLoadArray(tap+1, me.lengthRequired(tap+1)) = pumpLoad;
|
|
<span class="keyword">else</span>
|
|
pumpPosition = -12.0 * me.position(tap); <span class="comment">% + me.stretch(me.numTapers);</span>
|
|
me.currentDownholePosition = pumpPosition;
|
|
pumpLoad = me.load(tap) + me.force(me.numTapers);
|
|
me.currentDownholeLoad = pumpLoad;
|
|
status = 1;
|
|
<span class="keyword">end</span>
|
|
me.count(tap) = me.count(tap) - 1;
|
|
tapersAllowed = tapersAllowed + 1;
|
|
<span class="keyword">if</span> tapersAllowed > me.numTapers
|
|
tapersAllowed = me.numTapers;
|
|
<span class="keyword">end</span>
|
|
<span class="keyword">end</span>
|
|
tap = tap + 1;
|
|
<span class="keyword">end</span>
|
|
<span class="keyword">end</span>
|
|
</pre><h2 id="17">Check if the stroke has ended</h2><pre class="codeinput"> <span class="keyword">function</span> directionChanged = checkEndOfStroke(me, numConsecutivePoints)
|
|
directionChanged = 0;
|
|
tempDirection = me.DIRECTION_UNKNOWN;
|
|
startDirection = me.DIRECTION_UNKNOWN;
|
|
consecutivePointsAllSameDirection = 1;
|
|
|
|
<span class="keyword">if</span> me.positionSensor.history(1) > me.positionSensor.history(2)
|
|
tempDirection = me.DIRECTION_UP;
|
|
startDirection = me.DIRECTION_UP;
|
|
<span class="keyword">elseif</span> me.positionSensor.history(1) < me.positionSensor.history(2)
|
|
tempDirection = me.DIRECTION_DOWN;
|
|
startDirection = me.DIRECTION_DOWN;
|
|
<span class="keyword">end</span>
|
|
|
|
<span class="keyword">if</span> startDirection == me.DIRECTION_UP
|
|
|
|
<span class="keyword">for</span> i = 1:numConsecutivePoints
|
|
<span class="keyword">if</span> me.positionSensor.history(i) <= me.positionSensor.history(i+1)
|
|
consecutivePointsAllSameDirection = 0;
|
|
<span class="keyword">end</span>
|
|
<span class="keyword">end</span>
|
|
|
|
<span class="keyword">if</span> consecutivePointsAllSameDirection == 1
|
|
tempDirection = me.DIRECTION_UP;
|
|
<span class="keyword">end</span>
|
|
|
|
<span class="keyword">elseif</span> startDirection == me.DIRECTION_DOWN
|
|
|
|
<span class="keyword">for</span> i = 1:numConsecutivePoints
|
|
<span class="keyword">if</span> me.positionSensor.history(i) >= me.positionSensor.history(i+1)
|
|
consecutivePointsAllSameDirection = 0;
|
|
<span class="keyword">end</span>
|
|
<span class="keyword">end</span>
|
|
|
|
<span class="keyword">if</span> consecutivePointsAllSameDirection == 1
|
|
tempDirection = me.DIRECTION_DOWN;
|
|
<span class="keyword">end</span>
|
|
<span class="keyword">end</span>
|
|
|
|
<span class="keyword">if</span> tempDirection ~= me.lastDirection
|
|
<span class="keyword">if</span> (tempDirection == me.DIRECTION_UP) && <span class="keyword">...</span>
|
|
(me.currentCard.numPointsUsed > 1)
|
|
directionChanged = 1;
|
|
<span class="keyword">end</span>
|
|
me.lastDirection = tempDirection;
|
|
<span class="keyword">end</span>
|
|
<span class="keyword">end</span>
|
|
</pre><h2 id="18">Evaluation function for simulated position and load points</h2><pre class="codeinput"> <span class="keyword">function</span> 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, <span class="keyword">...</span>
|
|
me.positionSensor.history(2), me.loadSensor.lastValue);
|
|
<span class="keyword">if</span> d_status == 1
|
|
me.currentCard.push(me.positionSensor.lastValue, <span class="keyword">...</span>
|
|
me.loadSensor.lastValue, d_pos, d_load);
|
|
<span class="keyword">end</span>
|
|
|
|
<span class="keyword">if</span> me.checkEndOfStroke(5) == 1
|
|
me.endOfStroke()
|
|
<span class="keyword">end</span>
|
|
<span class="keyword">end</span>
|
|
</pre><h2 id="19">End of Stroke actions</h2><pre class="codeinput"> <span class="keyword">function</span> endOfStroke(me)
|
|
me.currentCard.calcStrokeData(100, me.fluidGradient, me.rodDepthTotal, <span class="keyword">...</span>
|
|
me.tubingAnchorDepth, me.tubingCrossSectionalArea, <span class="keyword">...</span>
|
|
me.pumpArea, me.frictionEstimate, <span class="keyword">...</span>
|
|
me.structuralRating, me.kFactor, me.waterBblRatio, me.oilBblRatio, <span class="keyword">...</span>
|
|
me.gasMcfRatio);
|
|
me.plotCards()
|
|
|
|
me.lastCard = me.currentCard();
|
|
me.currentCard = Card(me.lastCard.strokeNumber + 1);
|
|
<span class="keyword">end</span>
|
|
</pre><h2 id="20">Draws Cards</h2><p>Draws current card and shows last card</p><pre class="codeinput"> <span class="keyword">function</span> plotCards(me)
|
|
ax1 = subplot(2,2,1);
|
|
plot(me.lastCard.surfacePosition(1:me.lastCard.numPointsUsed), <span class="keyword">...</span>
|
|
me.lastCard.surfaceLoad(1:me.lastCard.numPointsUsed))
|
|
title(<span class="string">'Last Surface Card'</span>)
|
|
|
|
ax3 = subplot(2,2,3);
|
|
plot(me.lastCard.downholePosition(1:me.lastCard.numPointsUsed), <span class="keyword">...</span>
|
|
me.lastCard.downholeLoad(1:me.lastCard.numPointsUsed), <span class="string">'r'</span>)
|
|
title(<span class="string">'Last Downhole Card'</span>)
|
|
|
|
ax2 = subplot(2,2,2);
|
|
plot(me.currentCard.surfacePosition(1:me.currentCard.numPointsUsed), <span class="keyword">...</span>
|
|
me.currentCard.surfaceLoad(1:me.currentCard.numPointsUsed))
|
|
title(<span class="string">'Current Surface Card'</span>)
|
|
|
|
ax4 = subplot(2,2,4);
|
|
plot(me.currentCard.downholePosition(1:me.currentCard.numPointsUsed), <span class="keyword">...</span>
|
|
me.currentCard.downholeLoad(1:me.currentCard.numPointsUsed), <span class="string">'r'</span>)
|
|
title(<span class="string">'Current Downhole Card'</span>)
|
|
|
|
linkaxes([ax1, ax2, ax3, ax4], <span class="string">'x'</span>)
|
|
|
|
<span class="keyword">end</span>
|
|
</pre><pre class="codeinput"> <span class="keyword">end</span>
|
|
|
|
methods(Static)
|
|
</pre><h2 id="22">Determines Rod Weight Per Foot given Young's Modulus and Diameter</h2><pre class="codeinput"> <span class="keyword">function</span> wtPerFt = lookupRodWeightPerFoot(i_ym, i_diam)
|
|
YM_STEEL=30.5;
|
|
YM_FIBERGLASS=7.2;
|
|
wtPerFt = -1;
|
|
<span class="keyword">if</span> (i_ym == YM_STEEL)
|
|
<span class="keyword">if</span> (i_diam <= 2 && i_diam > 1.75)
|
|
wtPerFt = 10.7;
|
|
<span class="keyword">elseif</span> (i_diam <= 1.75 && i_diam > 1.65)
|
|
wtPerFt = 8.2;
|
|
<span class="keyword">elseif</span> (i_diam <= 1.65 && i_diam > 1.5)
|
|
wtPerFt = 7;
|
|
<span class="keyword">elseif</span> (i_diam <= 1.5 && i_diam > 1.375)
|
|
wtPerFt = 6;
|
|
<span class="keyword">elseif</span> (i_diam <= 1.375 && i_diam > 1.125)
|
|
wtPerFt = 5;
|
|
<span class="keyword">elseif</span> (i_diam <= 1.125 && i_diam > 1)
|
|
wtPerFt = 3.676;
|
|
<span class="keyword">elseif</span> (i_diam <= 1 && i_diam > 0.875)
|
|
wtPerFt = 2.904;
|
|
<span class="keyword">elseif</span> (i_diam <= 0.875 && i_diam > 0.75)
|
|
wtPerFt = 2.224;
|
|
<span class="keyword">elseif</span> (i_diam <= 0.75 && i_diam > 0.625)
|
|
wtPerFt = 1.634;
|
|
<span class="keyword">elseif</span> (i_diam <= 0.625 && i_diam > 0.5)
|
|
wtPerFt = 1.13;
|
|
<span class="keyword">elseif</span> (i_diam <= 0.5)
|
|
wtPerFt = 0.72;
|
|
<span class="keyword">else</span>
|
|
wtPerFt = 0;
|
|
<span class="keyword">end</span>
|
|
<span class="keyword">elseif</span> (i_ym == YM_FIBERGLASS)
|
|
<span class="keyword">if</span> (i_diam <= 1.25 && i_diam > 1.125)
|
|
wtPerFt = 1.2879;
|
|
<span class="keyword">elseif</span> (i_diam <= 1.125 && i_diam > 1)
|
|
wtPerFt = 1.09;
|
|
<span class="keyword">elseif</span> (i_diam <= 1 && i_diam > 0.875)
|
|
wtPerFt = 0.8188;
|
|
<span class="keyword">elseif</span> (i_diam <= 0.875 && i_diam > 0.75)
|
|
wtPerFt = 0.6108;
|
|
<span class="keyword">elseif</span> (i_diam <= 0.75)
|
|
wtPerFt = 0.484;
|
|
<span class="keyword">else</span>
|
|
wtPerFt = 0;
|
|
<span class="keyword">end</span>
|
|
<span class="keyword">end</span>
|
|
<span class="keyword">end</span>
|
|
</pre><pre class="codeinput"> <span class="keyword">end</span>
|
|
<span class="keyword">end</span>
|
|
</pre><p class="footer"><br><a href="http://www.mathworks.com/products/matlab/">Published with MATLAB® R2017a</a><br></p></div><!--
|
|
##### SOURCE BEGIN #####
|
|
classdef Well < handle
|
|
properties(Constant)
|
|
%% Constants (SHOULD THESE BE ELSEWHERE?)
|
|
YM_STEEL=30.5;
|
|
YM_FIBERGLASS=7.2;
|
|
|
|
DIRECTION_UNKNOWN=0;
|
|
DIRECTION_UP=1;
|
|
DIRECTION_DOWN=2;
|
|
end
|
|
|
|
properties
|
|
|
|
%% Instrumentation
|
|
device;
|
|
mux;
|
|
positionSensor;
|
|
loadSensor;
|
|
|
|
|
|
|
|
%% Current Values
|
|
currentSurfacePosition,currentSurfaceLoad;
|
|
currentDownholePosition,currentDownholeLoad;
|
|
currentCard=Card(0);
|
|
lastCard=Card(0);
|
|
direction=0;
|
|
lastDirection=0;
|
|
|
|
%% User Inputs
|
|
wellName;
|
|
dt = 1.0 / 30.0;
|
|
tubingHeadPressure = 40.0;
|
|
fluidGradient = 0.45;
|
|
stuffingBoxFriction = 100.0;
|
|
numTapers = 3;
|
|
tubingAnchorDepth = 0.0;
|
|
pumpDiameter = 2.5;
|
|
pumpArea;
|
|
tubingInnerDiameter = 1.995;
|
|
tubingOuterDiameter = 2.375;
|
|
tubingCrossSectionalArea;
|
|
structuralRating = 320000;
|
|
frictionEstimate;
|
|
|
|
%% Production Parameters
|
|
kFactor=1.0;
|
|
waterBblRatio=0.90;
|
|
oilBblRatio=0.10;
|
|
gasMcfRatio=5.0;
|
|
|
|
%% Rod String Inputs
|
|
c;
|
|
rodLength;
|
|
rodDiameter;
|
|
rodYM;
|
|
rodWeightPerFoot;
|
|
|
|
%% Calculated Taper Parameters
|
|
a;
|
|
area;
|
|
pressure;
|
|
stretch;
|
|
force;
|
|
alpha;
|
|
xOverA;
|
|
factorArray;
|
|
lagIndex;
|
|
lengthRequired;
|
|
centerPoint;
|
|
count;
|
|
|
|
buoyantForceTotal = 0.0;
|
|
rodDepthTotal = 0.0;
|
|
rodWeightAirTotal = 0.0;
|
|
rodWeightFluidTotal = 0.0;
|
|
|
|
end
|
|
|
|
properties(Access=private)
|
|
topPosArray;
|
|
topLoadArray;
|
|
|
|
% cross-function variables for position and load
|
|
% POSITION MUST ALWAYS BE CALLED FIRST.
|
|
loadBefore = 0.0;
|
|
loadAfter = 0.0;
|
|
loadBefore3 = 0.0;
|
|
loadAfter3 = 0.0;
|
|
tempLoadValue = 0.0;
|
|
end
|
|
|
|
methods
|
|
%% Constructor Function
|
|
function me = Well(name, sim)
|
|
me.wellName = name;
|
|
|
|
me.c = [0.8 0.8 0.8];
|
|
me.rodLength = [1475.0 1525.0 2000.0];
|
|
me.rodDiameter = [1.0 0.875 0.750];
|
|
me.rodYM = [me.YM_STEEL me.YM_STEEL me.YM_STEEL];
|
|
me.rodWeightPerFoot = [Well.lookupRodWeightPerFoot(me.rodYM(1), me.rodDiameter(1)) ...
|
|
Well.lookupRodWeightPerFoot(me.rodYM(2), me.rodDiameter(2)) ...
|
|
Well.lookupRodWeightPerFoot(me.rodYM(3), me.rodDiameter(3))];
|
|
|
|
% Initialize
|
|
me.a = zeros(1, me.numTapers, 'double');
|
|
me.area = zeros(1, me.numTapers+1, 'double');
|
|
me.pressure = zeros(1, me.numTapers, 'double');
|
|
me.stretch = zeros(1, me.numTapers, 'double');
|
|
me.force = zeros(1, me.numTapers, 'double');
|
|
me.alpha = zeros(1, me.numTapers, 'double');
|
|
me.xOverA = zeros(1, me.numTapers, 'double');
|
|
me.factorArray = zeros(1, me.numTapers, 'double');
|
|
me.lagIndex = zeros(1, me.numTapers, 'uint8');
|
|
me.lengthRequired = zeros(1, me.numTapers, 'uint8');
|
|
me.centerPoint = zeros(1, me.numTapers, 'uint8');
|
|
me.count = zeros(1, me.numTapers, 'uint8');
|
|
me.topPosArray = zeros(10, 250,'double');
|
|
me.topLoadArray = zeros(10, 250, 'double');
|
|
|
|
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
|
|
|
|
%% Well Setup Function
|
|
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
|
|
me.area(i) = pi / 4 * power(me.rodDiameter(i), 2);
|
|
end
|
|
|
|
for i = 1:me.numTapers
|
|
me.a(i) = 1000.0 * sqrt(32.2 * me.rodYM(i) * me.area(i) / me.rodWeightPerFoot(i));
|
|
me.rodDepthTotal = me.rodDepthTotal + me.rodLength(i);
|
|
|
|
if i > 1
|
|
me.pressure(i) = me.pressure(i - 1) + me.fluidGradient * me.rodLength(i);
|
|
else
|
|
me.pressure(i) = me.tubingHeadPressure + me.fluidGradient * me.rodLength(i);
|
|
end
|
|
|
|
me.buoyantForceTotal = me.buoyantForceTotal + me.pressure(i) * (me.area(i+1) - me.area(i));
|
|
me.rodWeightAirTotal = me.rodWeightAirTotal + me.rodWeightPerFoot(i) * me.rodLength(i);
|
|
me.rodWeightFluidTotal = me.rodWeightAirTotal + me.buoyantForceTotal;
|
|
end
|
|
|
|
for j = 1:me.numTapers
|
|
weightData = 0.0;
|
|
annularForceData = 0.0;
|
|
|
|
for i = j+1:me.numTapers
|
|
weightData = weightData + me.rodWeightPerFoot(i) * me.rodLength(i);
|
|
end
|
|
|
|
for i = j:me.numTapers-1
|
|
annularForceData = annularForceData + me.pressure(i) * (me.area(i) - me.area(i+1));
|
|
end
|
|
|
|
me.force(j) = (-me.area(me.numTapers) * me.pressure(me.numTapers)) + weightData - annularForceData;
|
|
me.alpha(j) = (me.force(j) + me.rodWeightPerFoot(j) * me.rodLength(j)) / (me.rodYM(j) * power(10, 6) * me.area(j));
|
|
|
|
if j > 1
|
|
me.stretch(j) = me.stretch(j - 1) + me.alpha(j) * me.rodLength(j) - ...
|
|
(me.rodWeightPerFoot(j) * power(me.rodLength(j), 2.0)) / ...
|
|
(2.0 * me.rodYM(j) * power(10, 6) * me.area(j));
|
|
else
|
|
me.stretch(j) = 0.0 + me.alpha(j) * me.rodLength(j) - ...
|
|
(me.rodWeightPerFoot(j) * power(me.rodLength(j), 2.0)) / ...
|
|
(2.0 * me.rodYM(j) * power(10, 6) * me.area(j));
|
|
end
|
|
end
|
|
|
|
for i = 1:me.numTapers
|
|
me.xOverA(i) = me.rodLength(i) / me.a(i);
|
|
me.lagIndex(i) = floor(me.rodLength(i) / (me.a(i) * me.dt));
|
|
me.factorArray(i) = (me.xOverA(i) - double(me.lagIndex(i)) * me.dt) / me.dt;
|
|
me.centerPoint(i) = me.lagIndex(i) + 2;
|
|
me.lengthRequired(i) = 2 * (me.lagIndex(i) + 1) + 1;
|
|
end
|
|
|
|
me.frictionEstimate = me.rodDepthTotal * 0.10;
|
|
end
|
|
|
|
%% Position Function (From Algorithm)
|
|
function pumpPosition = position(me, i)
|
|
% Position Function
|
|
ai = me.a(i);
|
|
ci = me.c(i);
|
|
factori = me.factorArray(i);
|
|
lengthi = me.rodLength(i);
|
|
lagi = double(me.lagIndex(i));
|
|
yi = me.rodYM(i) * power(10, 6);
|
|
areai = me.area(i);
|
|
centeri = me.centerPoint(i);
|
|
iBefore = centeri - me.lagIndex(i);
|
|
iAfter = centeri + me.lagIndex(i);
|
|
|
|
me.loadBefore = 0.0;
|
|
me.loadAfter = 0.0;
|
|
me.loadBefore3 = 0.0;
|
|
me.loadAfter3 = 0.0;
|
|
|
|
pumpPosition = exp(ci * lengthi / (2.0 * ai)) * (me.topPosArray(i, iAfter) + ...
|
|
factori * (me.topPosArray(i, iAfter+1) - me.topPosArray(i, iAfter)));
|
|
|
|
pumpPosition = pumpPosition + exp(-ci * lengthi / (2.0 * ai)) * ...
|
|
(me.topPosArray(i,iBefore) + factori * (me.topPosArray(i,iBefore-1) - ...
|
|
me.topPosArray(i, iBefore)));
|
|
|
|
pumpPosition = 0.5 * pumpPosition;
|
|
insideIntegral = 0.0;
|
|
|
|
for jj = 1:2*lagi-1
|
|
insideIntegral = insideIntegral + me.dt / (yi * areai) * ...
|
|
(exp((-ci * (lagi - jj) *me.dt) / 2.0) * ...
|
|
me.topLoadArray(i, iBefore + jj));
|
|
end
|
|
|
|
insideIntegral = insideIntegral + 0.5 * me.dt /(yi * areai) * ...
|
|
(exp((-ci * lagi * me.dt) / 2.0) * me.topLoadArray(i, iBefore) + ...
|
|
exp((-ci * -lagi * me.dt) / 2.0) * me.topLoadArray(i, iAfter));
|
|
|
|
me.loadBefore = exp((-ci * lagi * me.dt) / 2.0) * me.topLoadArray(i, iBefore) + ...
|
|
factori * (exp((-ci * (lagi + 1) * me.dt) / 2.0) * me.topLoadArray(i, iBefore-1) - ...
|
|
exp((-ci * lagi * me.dt) / 2.0) * me.topLoadArray(i, iBefore));
|
|
|
|
me.loadAfter = exp((-ci * -lagi * me.dt) / 2.0) * me.topLoadArray(i, iAfter) + ...
|
|
factori * (exp((-ci * (-lagi - 1) * me.dt) / 2.0) * me.topLoadArray(i, iAfter+1) - ...
|
|
exp((-ci * -lagi * me.dt) / 2.0) * me.topLoadArray(i,iAfter));
|
|
|
|
insideIntegral = insideIntegral + 0.5 * factori * me.dt / (yi * areai) * ...
|
|
(me.loadBefore + exp((-ci * lagi * me.dt) / 2.0) * me.topLoadArray(i, iBefore));
|
|
|
|
insideIntegral = insideIntegral + 0.5 * factori * me.dt / (yi * areai) * ...
|
|
(me.loadAfter + exp((-ci * -lagi * me.dt) / 2.0) * me.topLoadArray(i, iAfter));
|
|
|
|
insideIntegral = 0.5 * ai * insideIntegral;
|
|
pumpPosition = pumpPosition + insideIntegral;
|
|
|
|
insideIntegral = 0.0;
|
|
|
|
for jj = 1:2*lagi-1
|
|
insideIntegral = insideIntegral + me.dt * (exp((-ci * (lagi - jj) * me.dt) / 2.0) * ...
|
|
me.topPosArray(i, iBefore+jj));
|
|
end
|
|
|
|
insideIntegral = insideIntegral + 0.5 * me.dt * (exp((-ci * lagi * me.dt) / 2.0) * ...
|
|
me.topPosArray(i, iBefore) + exp((-ci * -lagi * me.dt) / 2.0) * ...
|
|
me.topPosArray(i, iAfter));
|
|
|
|
me.loadBefore3 = exp((-ci * lagi * me.dt) / 2.0) * me.topPosArray(i, iBefore) + ...
|
|
factori * (exp((-ci * (lagi+1) * me.dt) / 2.0) * me.topPosArray(i, iBefore-1) - ...
|
|
exp((-ci * lagi * me.dt) / 2.0) * me.topPosArray(i, iBefore));
|
|
|
|
me.loadAfter3 = exp((-ci * -lagi * me.dt) / 2.0) * me.topPosArray(i, iAfter) + ...
|
|
factori * (exp((-ci * (-lagi-1) * me.dt) / 2.0) * me.topPosArray(i, iAfter+1) - ...
|
|
exp((-ci * -lagi * me.dt) / 2.0) * me.topPosArray(i, iAfter));
|
|
|
|
insideIntegral = insideIntegral + 0.5 * factori * me.dt * (me.loadBefore3 + ...
|
|
exp((-ci * lagi * me.dt) / 2.0) * me.topPosArray(i, iBefore));
|
|
|
|
insideIntegral = insideIntegral + 0.5 * factori * me.dt * (me.loadAfter3 + ...
|
|
exp((-ci * -lagi * me.dt) / 2.0) * me.topPosArray(i, iAfter));
|
|
|
|
insideIntegral = -(ci * lengthi / 4) * 0.5 * (ci / (2.0 * ai)) * insideIntegral;
|
|
me.tempLoadValue = insideIntegral / lengthi;
|
|
pumpPosition = pumpPosition + insideIntegral;
|
|
end
|
|
|
|
%% Load Function (From Algorithm)
|
|
function pumpLoad = load(me, i)
|
|
% Load Function
|
|
ai = me.a(i);
|
|
ci = me.c(i);
|
|
lengthi = me.rodLength(i);
|
|
lagi = me.lagIndex(i);
|
|
yi = me.rodYM(i) * power(10, 6);
|
|
areai = me.area(i);
|
|
centeri = me.centerPoint(i);
|
|
iBefore = centeri - lagi;
|
|
iAfter = centeri + lagi;
|
|
|
|
pumpLoad = 0.5 * (ai / (yi * areai)) * (1 / ai) * (me.loadBefore + me.loadAfter);
|
|
tempResult = yi * areai * pumpLoad;
|
|
pumpLoad = pumpLoad - (ci * lengthi / 4) * (0.5 * (ci / (2.0 * ai))) * (1 / ai) * ...
|
|
(me.loadBefore3 + me.loadAfter3);
|
|
|
|
ptAfter = (me.topPosArray(i, iAfter+1) - me.topPosArray(i, iAfter-1)) / (2.0 * me.dt);
|
|
ptBefore = (me.topPosArray(i, iBefore+1) - me.topPosArray(i, iBefore-1)) / (2.0 * me.dt);
|
|
firstPart = (exp((ci * lengthi) / (2.0 * ai)) * ptAfter - exp((-ci * lengthi) / ...
|
|
(2.0 * ai)) * ptBefore) / (2.0 * ai);
|
|
|
|
firstPart = firstPart + (ci * exp((ci * lengthi) / (2.0 * ai)) * ...
|
|
me.topPosArray(i, iAfter) - ci * exp((-ci * lengthi) / (2.0 * ai)) * ...
|
|
me.topPosArray(i, iBefore)) / (4 * ai);
|
|
|
|
pumpLoad = yi * areai * (firstPart + pumpLoad); % + tempLoadValue ?
|
|
end
|
|
|
|
%% Calc Function
|
|
% Calls position and load functions
|
|
function [pumpPosition, pumpLoad, status] = calc(me, polishedRodPosition, lastPolishedRodPosition,...
|
|
polishedRodLoad)
|
|
me.currentSurfacePosition = polishedRodPosition;
|
|
me.currentSurfaceLoad = polishedRodLoad;
|
|
pumpPosition = -1.0;
|
|
pumpLoad = -1.0;
|
|
status = -1;
|
|
loadMult = 1.0;
|
|
tapersAllowed = 1;
|
|
|
|
for ii = 2:me.lengthRequired(1)
|
|
me.topPosArray(1, ii-1) = me.topPosArray(1, ii);
|
|
me.topLoadArray(1, ii-1) = me.topLoadArray(1, ii);
|
|
end
|
|
|
|
me.topPosArray(1, me.lengthRequired(1)) = -1.0 * polishedRodPosition / 12.0;
|
|
|
|
if polishedRodPosition > lastPolishedRodPosition
|
|
me.topLoadArray(1, me.lengthRequired(1)) = loadMult * ...
|
|
(polishedRodLoad - me.rodWeightFluidTotal) - me.stuffingBoxFriction;
|
|
elseif polishedRodPosition < lastPolishedRodPosition
|
|
me.topLoadArray(1, me.lengthRequired(1)) = loadMult * ...
|
|
(polishedRodLoad - me.rodWeightFluidTotal) + me.stuffingBoxFriction;
|
|
else
|
|
me.topLoadArray(1, me.lengthRequired(1)) = loadMult * ...
|
|
(polishedRodLoad - me.rodWeightFluidTotal);
|
|
end
|
|
|
|
tap=1;
|
|
while tap <= tapersAllowed
|
|
me.count(tap) = me.count(tap) + 1;
|
|
if me.count(tap) >= me.lengthRequired(tap)
|
|
if tap+1 <= me.numTapers
|
|
% working our way down to the bottom of the well
|
|
for pti = 2:me.lengthRequired(tap+1)+1
|
|
me.topPosArray(tap+1, pti-1) = me.topPosArray(tap+1, pti);
|
|
me.topLoadArray(tap+1, pti-1) = me.topLoadArray(tap+1, pti);
|
|
end
|
|
pumpPosition = me.position(tap);
|
|
pumpLoad = me.load(tap);
|
|
status = 0;
|
|
me.topPosArray(tap+1, me.lengthRequired(tap+1)) = pumpPosition;
|
|
me.topLoadArray(tap+1, me.lengthRequired(tap+1)) = pumpLoad;
|
|
else
|
|
pumpPosition = -12.0 * me.position(tap); % + me.stretch(me.numTapers);
|
|
me.currentDownholePosition = pumpPosition;
|
|
pumpLoad = me.load(tap) + me.force(me.numTapers);
|
|
me.currentDownholeLoad = pumpLoad;
|
|
status = 1;
|
|
end
|
|
me.count(tap) = me.count(tap) - 1;
|
|
tapersAllowed = tapersAllowed + 1;
|
|
if tapersAllowed > me.numTapers
|
|
tapersAllowed = me.numTapers;
|
|
end
|
|
end
|
|
tap = tap + 1;
|
|
end
|
|
end
|
|
|
|
%% Check if the stroke has ended
|
|
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
|
|
|
|
|
|
%% Evaluation function for simulated position and load points
|
|
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
|
|
|
|
%% End of Stroke actions
|
|
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
|
|
|
|
%% Draws Cards
|
|
% Draws current card and shows last card
|
|
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
|
|
|
|
methods(Static)
|
|
%% Determines Rod Weight Per Foot given Young's Modulus and Diameter
|
|
function wtPerFt = lookupRodWeightPerFoot(i_ym, i_diam)
|
|
YM_STEEL=30.5;
|
|
YM_FIBERGLASS=7.2;
|
|
wtPerFt = -1;
|
|
if (i_ym == YM_STEEL)
|
|
if (i_diam <= 2 && i_diam > 1.75)
|
|
wtPerFt = 10.7;
|
|
elseif (i_diam <= 1.75 && i_diam > 1.65)
|
|
wtPerFt = 8.2;
|
|
elseif (i_diam <= 1.65 && i_diam > 1.5)
|
|
wtPerFt = 7;
|
|
elseif (i_diam <= 1.5 && i_diam > 1.375)
|
|
wtPerFt = 6;
|
|
elseif (i_diam <= 1.375 && i_diam > 1.125)
|
|
wtPerFt = 5;
|
|
elseif (i_diam <= 1.125 && i_diam > 1)
|
|
wtPerFt = 3.676;
|
|
elseif (i_diam <= 1 && i_diam > 0.875)
|
|
wtPerFt = 2.904;
|
|
elseif (i_diam <= 0.875 && i_diam > 0.75)
|
|
wtPerFt = 2.224;
|
|
elseif (i_diam <= 0.75 && i_diam > 0.625)
|
|
wtPerFt = 1.634;
|
|
elseif (i_diam <= 0.625 && i_diam > 0.5)
|
|
wtPerFt = 1.13;
|
|
elseif (i_diam <= 0.5)
|
|
wtPerFt = 0.72;
|
|
else
|
|
wtPerFt = 0;
|
|
end
|
|
elseif (i_ym == YM_FIBERGLASS)
|
|
if (i_diam <= 1.25 && i_diam > 1.125)
|
|
wtPerFt = 1.2879;
|
|
elseif (i_diam <= 1.125 && i_diam > 1)
|
|
wtPerFt = 1.09;
|
|
elseif (i_diam <= 1 && i_diam > 0.875)
|
|
wtPerFt = 0.8188;
|
|
elseif (i_diam <= 0.875 && i_diam > 0.75)
|
|
wtPerFt = 0.6108;
|
|
elseif (i_diam <= 0.75)
|
|
wtPerFt = 0.484;
|
|
else
|
|
wtPerFt = 0;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
##### SOURCE END #####
|
|
--></body></html> |