//: Playground - noun: a place where people can play import UIKit 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>(); var topLoadArray = Array>(); var NumColumns = 100; var NumRows = 10; for (var i = 0; i < NumColumns; i++) { topPosArray.append(Array(count:NumRows, repeatedValue:Double())) topLoadArray.append(Array(count:NumRows, repeatedValue:Double())) } 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; 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; } init(){ } // 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, '&'); //} //} }