Files
POC-Swift/POC.playground/Contents.swift
Patrick McDonagh 8b3911d728 Initial Commit
2016-07-01 17:00:11 -05:00

761 lines
26 KiB
Swift

//: Playground - noun: a place where people can play
import Cocoa
var test = "Hello";
struct LoadSpan //structure to hold load spans
{
var top:Double;
var bottom:Double;
var span:Double;
};
func findMax(arr:[Double], len:Int) -> Double
// find the maximum value of a given array of length len
{
var max = arr[0];
for(var i = 0; i < len; i++)
{
if (arr[i] > max)
{
max = arr[i];
}
}
return max;
};
func findMin(arr:[Double], len:Int) -> Double
// find the min value of a given array of length len
{
var min = arr[0];
for(var i = 0; i < len; i++)
{
if(arr[i] < min)
{
min = arr[i];
}
}
return min;
};
func updAverage(prevAverage:Double, currentMeasurement:Double, numMeasurements:Int) -> Double
{
var newAverage:Double;
newAverage = prevAverage * ((Double(numMeasurements) - 1)/Double(numMeasurements)) + (currentMeasurement / Double(numMeasurements));
return newAverage;
};
class LPPair //structure to hold Load and Position
{
var load:Double;
var position:Double;
func clr()
{
load = 0;
position = 0;
}
init(){
clr();
}
};
class WellData
{
var polishedRodHorsepower:Double;
var pumpHorsepower:Double;
var pumpIntakePressure:Double;
var fluidLevel:Double;
var strokeSPM:Double;
var strokeNumber:Int;
var surfacePositionMax: LPPair;
var surfacePositionMin: LPPair;
var surfaceLoadMax: LPPair;
var surfaceLoadMin: LPPair;
var downholePositionMax: LPPair;
var downholePositionMin: LPPair;
var downholeLoadMax: LPPair;
var downholeLoadMin: LPPair;
var topRight: LPPair;
var topLeft: LPPair;
var fillPoint: LPPair;
var fullFillPoint: LPPair;
var bottomLeft: LPPair;
var surfaceStrokeLength:Double;
var downholeNetStrokeLength:Double;
var downholeGrossStrokeLength:Double;
var downholeAdjustedGrossStrokeLength:Double;
var downholeLoadSpan:Double;
var fluidLoad:Double;
var fillageEstimated:Double;
var fillageCalculated:Double;
var tubingMovement:Double;
var plungerTravel:Double;
var dailyProduction:Double;
var structuralLoading:Double;
func clearAll()
{
polishedRodHorsepower = 0;
pumpHorsepower = 0;
pumpIntakePressure = 0;
fluidLevel = 0;
strokeSPM = 0;
strokeNumber = 1;
surfacePositionMax.clr();
surfacePositionMin.clr();
surfaceLoadMax.clr();
surfaceLoadMin.clr();
downholePositionMax.clr();
downholePositionMin.clr();
downholeLoadMax.clr();
downholeLoadMin.clr();
topRight.clr();
topLeft.clr();
fillPoint.clr();
fullFillPoint.clr();
bottomLeft.clr();
surfaceStrokeLength = 0;
downholeNetStrokeLength = 0;
downholeGrossStrokeLength = 0;
downholeAdjustedGrossStrokeLength = 0;
downholeLoadSpan = 0;
fluidLoad = 0;
fillageEstimated = 0;
fillageCalculated = 0;
tubingMovement = 0;
plungerTravel = 0;
dailyProduction = 0;
structuralLoading = 0;
};
init(){
clearAll();
}
};
class Card
//Object for card calculations and data
{
var surfacePosition = [Double](count: 1000, repeatedValue: 0.0);;
var downholePosition = [Double](count: 1000, repeatedValue: 0.0);;
var surfaceLoad = [Double](count: 1000, repeatedValue: 0.0);;
var downholeLoad = [Double](count: 1000, repeatedValue: 0.0);;
var data:WellData;
var pointCounter:Int;
func findLoadAtPosition(pos:Double, posArray:[Double], loadArray: [Double], size:Int) -> Double
{
for(var i = 0; i < size; i++)
{
if(posArray[i] == pos){
return loadArray[i];
}
}
};
func findPositionAtLoad(load:Double, posArray:[Double], loadArray: [Double], size:Int) -> Double
{
for(var i = 0; i < size; i++)
{
if(loadArray[i] == load){
return posArray[i];
}
}
};
func findLimits()
{
//find maximum values of the arrays
data.surfacePositionMax.position = findMax(surfacePosition, pointCounter);
data.surfacePositionMax.load = findLoadAtPosition(data.surfacePositionMax.position, posArray: surfacePosition,loadArray: surfaceLoad ,size: pointCounter);
data.surfaceLoadMax.load = findMax(surfaceLoad, pointCounter);
data.surfaceLoadMax.position = findPositionAtLoad(data.surfaceLoadMax.load, posArray: surfacePosition, loadArray: surfaceLoad ,size: pointCounter);
data.downholePositionMax.position = findMax(downholePosition, pointCounter);
data.downholePositionMax.load = findLoadAtPosition(data.downholePositionMax.position, posArray: downholePosition, loadArray: downholeLoad ,size: pointCounter);
data.downholeLoadMax.load = findMax(downholeLoad, pointCounter);
data.downholeLoadMax.position = findPositionAtLoad(data.downholeLoadMax.load, posArray: downholePosition, loadArray: downholeLoad, size: pointCounter);
//find minimum values of the arrays
data.surfacePositionMin.position = findMin(surfacePosition, pointCounter);
data.surfacePositionMin.load = findLoadAtPosition(data.surfacePositionMin.position, posArray: surfacePosition, loadArray: surfaceLoad, size: pointCounter);
data.surfaceLoadMin.load = findMin(surfaceLoad, pointCounter);
data.surfaceLoadMin.position = findPositionAtLoad(data.surfaceLoadMin.load, posArray: surfacePosition, loadArray: surfaceLoad, size: pointCounter);
data.downholePositionMin.position = findMin(downholePosition, pointCounter);
data.downholePositionMin.load = findLoadAtPosition(data.downholePositionMin.position, posArray: downholePosition, loadArray: downholeLoad, size: pointCounter);
data.downholeLoadMin.load = findMin(downholeLoad, pointCounter);
data.downholeLoadMin.position = findPositionAtLoad(data.downholeLoadMin.load, posArray: downholePosition, loadArray: downholeLoad, size: pointCounter);
data.surfaceStrokeLength = data.surfacePositionMax.position - data.surfacePositionMin.position;
data.downholeGrossStrokeLength = data.downholePositionMax.position - data.downholePositionMin.position;
data.downholeLoadSpan = data.downholeLoadMax.load - data.downholeLoadMin.load;
};
func findNearestPointToLoad(direction:String, cardHalf:String, targetLoad:Double, posArray:[Double], loadArray:[Double], size:Int) -> LPPair
{
var posMin = findMin(posArray, size);
var posMax = findMax(posArray, size);
var leftIgnore = posMin + (posMax - posMin)*(0.75);
var rightIgnore = posMin + (posMax - posMin)*(0.25);
var foundPoint:LPPair;
if (direction=="up")
{
for(var i = 1; i < size; i++)
{
if(cardHalf == "right" && posArray[i] >= rightIgnore)
{
if(loadArray[i] <= targetLoad && loadArray[i-1] > targetLoad)
{
foundPoint.position = posArray[i];
foundPoint.load = loadArray[i];
return foundPoint;
}
}
else
{
if(loadArray[i] >= targetLoad && loadArray[i-1] < targetLoad)
{
foundPoint.position = posArray[i];
foundPoint.load = loadArray[i];
return foundPoint;
}
}
}
}
else {
for(var i = 1; i < size; i++)
{
if(cardHalf == "right" && posArray[i] >= rightIgnore)
{
if(loadArray[i] >= targetLoad && loadArray[i-1] < targetLoad)
{
foundPoint.position = posArray[i];
foundPoint.load = loadArray[i];
return foundPoint;
}
}
else
{
if(loadArray[i] <= targetLoad && loadArray[i-1] > targetLoad)
{
foundPoint.position = posArray[i];
foundPoint.load = loadArray[i];
return foundPoint;
}
}
}
}
}
func findIncrementalLoad(targetPosition:Double, posArray:[Double], loadArray:[Double], size:Int) -> LoadSpan
{
var result:LoadSpan;
var upPointGreater:LPPair;
var upPointLesser:LPPair;
var downPointGreater:LPPair;
var downPointLesser:LPPair;
for(var i = 1; i < size; i++)
{
if ((posArray[i] >= targetPosition) && (posArray[i-1] < targetPosition))
{
upPointGreater.position = posArray[i];
upPointGreater.load = loadArray[i];
upPointLesser.position = posArray[i-1];
upPointLesser.load = loadArray[i-1];
}
if ((posArray[i] <= targetPosition) && (posArray[i-1] > targetPosition))
{
downPointGreater.position = posArray[i];
downPointGreater.load = loadArray[i];
downPointLesser.position = posArray[i-1];
downPointLesser.load = loadArray[i-1];
}
var mUp = (upPointGreater.load - upPointLesser.load) / (upPointGreater.position - upPointLesser.position);
var bUp = upPointGreater.load - (mUp * upPointGreater.position);
result.top = (mUp * targetPosition) + bUp;
var mDown = (downPointGreater.load - downPointLesser.load) / (downPointGreater.position - downPointLesser.position);
var bDown = downPointGreater.load - (mDown * downPointGreater.position);
result.bottom = (mDown * targetPosition) + bDown;
result.span = result.top - result.bottom;
return result;
}
}
func findCorners(pctTop:Double, pctBottom:Double)
{
var halfLoadLine = data.downholeLoadMin.load + data.downholeLoadSpan/2;
var halfLoad:LPPair;
halfLoad = findNearestPointToLoad("up", cardHalf: "left", targetLoad: halfLoadLine, posArray: downholePosition, loadArray: downholeLoad, size: pointCounter);
var topLoadLine = data.downholeLoadMax.load - (data.downholeLoadSpan * (pctTop/100));
var bottomLoadLine = data.downholeLoadMin.load + (data.downholeLoadSpan * (pctBottom/100));
if(data.downholePositionMin.position < halfLoad.position)
{
data.bottomLeft = data.downholePositionMin;
data.topLeft = findNearestPointToLoad("up", cardHalf: "left", targetLoad: topLoadLine, posArray: downholePosition, loadArray: downholeLoad, size: pointCounter);
}
else
{
data.bottomLeft = findNearestPointToLoad("up", cardHalf: "left", targetLoad: bottomLoadLine, posArray: downholePosition, loadArray: downholeLoad, size: pointCounter);
data.topLeft = data.downholePositionMin;
}
data.topRight = data.downholePositionMax;
data.fillPoint = findNearestPointToLoad("up", cardHalf: "right", targetLoad: bottomLoadLine, posArray: downholePosition, loadArray: downholeLoad, size: pointCounter);
data.fullFillPoint.position = data.bottomLeft.position + data.topRight.position - data.topLeft.position;
data.fullFillPoint.load = data.bottomLeft.load;
};
func calcFillage(anchorDepth:Double, rodDepth:Double, tubingCSA:Double)
{
data.tubingMovement = 12 * (rodDepth - anchorDepth)*data.fluidLoad / (30500000 * tubingCSA);
data.downholeAdjustedGrossStrokeLength = data.downholeGrossStrokeLength - data.tubingMovement;
data.downholeNetStrokeLength = data.fillPoint.position - data.bottomLeft.position;
data.fillageEstimated = ((data.fillPoint.position - data.bottomLeft.position)/(data.fullFillPoint.position - data.bottomLeft.position))*100.0;
data.fillageCalculated = ((data.downholeNetStrokeLength + data.tubingMovement)/data.downholeGrossStrokeLength)*100.0;
if (data.fillageEstimated > 100)
{
data.fillageEstimated = 100.0;
}
if (data.fillageCalculated > 100)
{
data.fillageCalculated = 100.0;
}
};
func calcHorsepower (riemannSlices:Int)
{
data.polishedRodHorsepower = 0.0;
data.pumpHorsepower = 0.0;
var topSlices = [Double](count: riemannSlices, repeatedValue: 0.0);
var bottomSlices = [Double](count: riemannSlices, repeatedValue: 0.0);
var dxSurface = data.surfaceStrokeLength / (Double(riemannSlices) + 1);
var dxDownhole = data.downholeNetStrokeLength / (Double(riemannSlices) + 1);
for (var i = 1; i < riemannSlices; i++)
{
var targetSurface = dxSurface * Double(i) + data.surfacePositionMin.position;
var targetDownhole = dxDownhole * Double(i) + data.downholePositionMin.position;
var sliceSurface:LoadSpan;
var sliceDownhole:LoadSpan;
sliceSurface = findIncrementalLoad(targetSurface, posArray: surfacePosition, loadArray: surfaceLoad, size: pointCounter);
sliceDownhole = findIncrementalLoad(targetDownhole, posArray: downholePosition, loadArray: downholeLoad, size: pointCounter);
topSlices[i] = sliceDownhole.top;
bottomSlices[i] = sliceDownhole.bottom;
data.polishedRodHorsepower += (dxSurface/12) * sliceSurface.span * data.strokeSPM / 33000;
data.pumpHorsepower += abs((dxDownhole / 12) * sliceDownhole.span * data.strokeSPM / 33000);
}
};
func calcFluidLevel(fluidGradient:Double, rodDepth:Double, pumpArea:Double, frictionEstimate:Double)
{
data.fluidLoad = (data.downholeLoadMax.load - data.downholeLoadMin.load) - frictionEstimate;
data.pumpIntakePressure = fluidGradient * rodDepth - (data.fluidLoad / pumpArea);
//printf("PIP = %f * %f - (%f / %f) = %f\n", fluidGradient, rodDepth, data.fluidLoad, pumpArea, data.pumpIntakePressure);
data.fluidLevel = data.pumpIntakePressure / fluidGradient;
};
func calcLoading(structuralRating:Double)
{
data.structuralLoading = (data.surfaceLoadMax.load / (structuralRating * 100)) * 100;
}
init(){
}
};
class Well
//well object holding all pertinant calculations for the well
{
var currentCard:Card;
var topPosArray = Array<Array<Double>>();
var topLoadArray = Array<Array<Double>>();
var NumColumns = 100;
var NumRows = 10;
var loadBefore:Double;
var loadAfter:Double;
var loadBefore3:Double;
var loadAfter3:Double;
var dt:Double; //delta T for measurements
var tubingHeadPressure:Double; //Tubing Head Pressure (PSI)
var fluidGradient:Double; //Tubing Fluid Gradient (PSI/ft)
var sbfriction:Double; //Stuffing Box Friction (lbs)
var numTapers:Int; //Number of Rod String Tapers
var nT1:Int; //number of tapers + 1
var anchorDepth:Double;
var pumpDiameter:Double;
var pumpArea:Double;
var tubingID:Double;
var tubingOD:Double;
var tubingCrossSectionalArea:Double;
var frictionEstimate:Double;
var theoreticalMaxFluidLoad:Double;
var structuralRating:Double;
//arrays of size nT1 due to addresses starting at 0
//Rod String Inputs
var c = [Double](count: 11, repeatedValue: 0.0); //User-Defined damping constant per taper (1/sec)
var rodLength = [Double](count: 11, repeatedValue: 0.0); //Length of each rod taper (top to bottom)(ft)
var rodDiameter = [Double](count: 11, repeatedValue: 0.0); //Diameter of each rod taper (top to bottom)(in)
var rodYM = [Double](count: 11, repeatedValue: 0.0); //Youngs modulus of each rod taper (top to bottom)(PSI) Steel=30.5, fiberglass=7.2
var rodWeightPerFoot = [Double](count: 11, repeatedValue: 0.0); //Weight per foot of each rod (ft/lb)
var a = [Double](count: 11, repeatedValue: 0.0);
var area = [Double](count: 12, repeatedValue: 0.0);
var pressure = [Double](count: 11, repeatedValue: 0.0);
var buoyantForce = [Double](count: 11, repeatedValue: 0.0);
var buoyantForceTotal:Double;
var stretch = [Double](count: 11, repeatedValue: 0.0);
var weightData = [Double](count: 11, repeatedValue: 0.0);
var weightDataTotal:Double;
var annularForceData = [Double](count: 11, repeatedValue: 0.0);
var annularForceDataTotal:Double;
var force = [Double](count: 11, repeatedValue: 0.0);
var alpha = [Double](count: 11, repeatedValue: 0.0);
var xOverA = [Double](count: 11, repeatedValue: 0.0);
var factorArray = [Double](count: 11, repeatedValue: 0);
var lagIndexArray = [Int](count: 11, repeatedValue: 0);
var centerPoint = [Int](count: 11, repeatedValue: 0);
var sumCenterPoint = [Int](count: 11, repeatedValue: 0);
var lengthRequired = [Int](count: 11, repeatedValue: 0);
var rodDepth = [Double](count: 11, repeatedValue: 0.0);
var rodDepthTotal:Double;
var rodWeightAir = [Double](count: 11, repeatedValue: 0.0);
var rodWeightAirTotal:Double;
var rodWeightFluid = [Double](count: 11, repeatedValue: 0.0);
var rodWeightFluidTotal:Double;
var count = [Int](count: 11, repeatedValue: 0);
var sPositionPrevious:Double;
init(){
topArraySetup()
}
func topArraySetup(){
for (var i = 0; i < NumColumns; i++)
{
topPosArray.append(Array(count:NumRows, repeatedValue:Double()))
topLoadArray.append(Array(count:NumRows, repeatedValue:Double()))
}
}
func printTaperSetup()
//prints the current taper setup to the console
//TODO: update this to print to a file.
{
println("Taper Parameters");
println(NSString(format: "dt: %.2f", dt))
println(NSString(format: "tubingHeadPressure: %.2f", tubingHeadPressure))
println(NSString(format: "fluidGradient: %.2f", fluidGradient))
println(NSString(format: "sbfriction: %.2f", sbfriction))
println(NSString(format: "numTapers: %.2f", numTapers))
println("c:")
dump(c);
println("rodLength:")
dump(rodLength)
println("rodDiameter:")
dump(rodDiameter);
println("rodYM:")
dump(rodYM);
println("rodWeightPerFoot:")
dump(rodWeightPerFoot)
println("a:");
dump(a);
println("area:")
dump(area)
println("pressure:")
dump(pressure)
println("buoyantForce:")
dump(buoyantForce)
println("stretch")
dump(stretch)
println("weightData")
dump(weightData)
println("annularForceData:")
dump(annularForceData)
println("force:")
dump(force)
println("alpha:")
dump(alpha)
println("xOverA:")
dump(xOverA)
println("factorArray")
dump(factorArray)
println("lagIndexArray")
dump(lagIndexArray)
println("centerPoint:")
dump(centerPoint)
println("sumCenterPoint:")
dump(sumCenterPoint)
println("lengthRequired:")
dump(lengthRequired)
println("rodDepth")
dump(rodDepth)
println("rodWeightAir")
dump(rodWeightAir)
println("rodWeightFluid")
dump(rodWeightFluid)
println(NSString(format: "buoyantForceTotal: %.2f", buoyantForceTotal))
println(NSString(format: "weightDataTotal: %.2f", weightDataTotal))
println(NSString(format: "annularForceDataTotal: %.2f", annularForceDataTotal))
println(NSString(format: "rodDepthTotal: %.2f", rodDepthTotal))
println(NSString(format: "rodWeightAirTotal: %.2f", rodWeightAirTotal))
println(NSString(format: "rodWeightFluidTotal: %.2f", rodWeightFluidTotal))
println(NSString(format: "tubingID: %.2f", tubingID))
println(NSString(format: "tubingOD: %.2f", tubingOD))
println(NSString(format: "tubingCSA: %.2f", tubingCrossSectionalArea))
println(NSString(format: "structuralRating: %.2f", structuralRating))
}
func lookupRodWeightPerFoot(i_ym:Double, i_diam:Double) -> Double
//looks up rod Weight per foot
{
var wtPerFt:Double;
if (i_ym == 30.5)
{
if (i_diam <= 2 && i_diam > 1.75)
{
wtPerFt = 10.7;
}
else if (i_diam <= 1.75 && i_diam > 1.65)
{
wtPerFt = 8.2;
}
else if (i_diam <= 1.65 && i_diam > 1.5)
{
wtPerFt = 7;
}
else if (i_diam <= 1.5 && i_diam > 1.375)
{
wtPerFt = 6;
}
else if (i_diam <= 1.375 && i_diam > 1.125)
{
wtPerFt = 5;
}
else if (i_diam <= 1.125 && i_diam > 1)
{
wtPerFt = 3.676;
}
else if (i_diam <= 1 && i_diam > 0.875)
{
wtPerFt = 2.904;
}
else if (i_diam <= 0.875 && i_diam > 0.75)
{
wtPerFt = 2.224;
}
else if (i_diam <= 0.75 && i_diam > 0.625)
{
wtPerFt = 1.634;
}
else if (i_diam <= 0.625 && i_diam > 0.5)
{
wtPerFt = 1.13;
}
else if (i_diam <= 0.5)
{
wtPerFt = 0.72;
}
else
{
wtPerFt = 0;
}
}
else if (i_ym == 7.2)
{
if (i_diam <= 1.25 && i_diam > 1.125)
{
wtPerFt = 1.2879;
}
else if (i_diam <= 1.125 && i_diam > 1)
{
wtPerFt = 1.09;
}
else if (i_diam <= 1 && i_diam > 0.875)
{
wtPerFt = 0.8188;
}
else if (i_diam <= 0.875 && i_diam > 0.75)
{
wtPerFt = 0.6108;
}
else if (i_diam <= 0.75)
{
wtPerFt = 0.484;
}
else
{
wtPerFt = 0;
}
}
else
{
wtPerFt = 0;
}
return wtPerFt;
}
// func readTaperCSV(filename:String)
// //reads the rod string configuration from a CSV
// {
//
// let csvURL = NSURL(string: "/Users/patrickjmcd/wellParamsIn_1.csv")!
// var error: NSErrorPointer = nil
// let csv = CSV(contentsOfURL: csvURL, error: error)
//
// // Rows
// let rows = csv.rows
//
// for(var row in csv.rows){
//
// }
// Float parseFloat(std::string dblStr);
// Float parseFloatString(Float *arr, std::string dblStr, int len, char delim );
//
// std::ifstream file (filename.c_str());
//
// while ( file.good() )
// {
// std::string key;
// std::string value;
// getline( file, key, ',' );
// getline( file, value, '\n');
//
// if(key.compare("dt")==0){
// dt = parseFloat(value);
// }
// else if (key.compare("tubingHeadPressure")==0)
// {
// tubingHeadPressure = parseFloat(value);
// }
// else if (key.compare("fluidGradient")==0)
// {
// fluidGradient = parseFloat(value);
// }
// else if (key.compare("sbfriction")==0)
// {
// sbfriction = parseFloat(value);
// }
// else if (key.compare("tubingID")==0)
// {
// tubingID = parseFloat(value);
// }
// else if (key.compare("tubingOD")==0)
// {
// tubingOD = parseFloat(value);
// }
// else if (key.compare("numTapers")==0)
// {
// std::istringstream(value) >> numTapers;
// }
// else if (key.compare("anchorDepth")==0)
// {
// anchorDepth = parseFloat(value);
// }
// else if (key.compare("pumpDiameter")==0)
// {
// pumpDiameter = parseFloat(value);
// pumpArea = pow(pumpDiameter,2) * M_PI;
// }
// else if (key.compare("structuralRating")==0)
// {
// structuralRating = parseFloat(value);
// }
// else if (key.compare("c")==0)
// {
// parseFloatString(c, value, 10, '&');
//}
//else if (key.compare("rodLength")==0)
//{
// parseFloatString(rodLength, value, 10, '&');
//}
//else if (key.compare("rodDiameter")==0)
//{
// parseFloatString(rodDiameter, value, 10, '&');
//}
//else if (key.compare("rodYM")==0)
//{
// parseFloatString(rodYM, value, 10, '&');
//}
//}
}
var well:Well;
well.printTaperSetup();
println(well.numTapers);