package com.henrypump.poc; import de.vandermeer.asciitable.v2.RenderedTable; import de.vandermeer.asciitable.v2.V2_AsciiTable; import de.vandermeer.asciitable.v2.render.V2_AsciiTableRenderer; import de.vandermeer.asciitable.v2.render.WidthAbsoluteEven; import de.vandermeer.asciitable.v2.themes.V2_E_TableThemes; import static java.lang.Math.abs; import static java.lang.Math.pow; import static java.lang.Math.sqrt; /** * Created by patrickjmcd on 1/31/17. */ public class Card { // Card Points private int maxPoints = 1500; private double[] surfacePosition = new double[maxPoints]; private double[] downholePosition = new double[maxPoints]; private double[] surfaceLoad = new double[maxPoints]; private double[] downholeLoad = new double[maxPoints]; private int numPointsUsed; //Card private long strokeNumber; private LPPair surfacePositionMax; private LPPair surfacePositionMin; private LPPair surfaceLoadMax; private LPPair surfaceLoadMin; private LPPair downholePositionMax; private LPPair downholePositionMin; private LPPair downholeLoadMax; private LPPair downholeLoadMin; private LPPair topCorner; private LPPair bottomCorner; private double surfaceStrokeLength; private double downholeNetStrokeLength; private double downholeGrossStrokeLength; private double downholeAdjustedGrossStrokeLength; private double downholeLoadSpan; private double fluidLoad; private double pumpIntakePressure; private double fluidLevel; private double fillageEstimated; private double fillageCalculated; private double tubingMovement; private double strokeSpeed; private double structuralLoading; private double polishedRodHorsepower; private double pumpHorsepower; private double fluidBBLMoved; private double fluidBBLMovedAdjusted; private double waterBBLMoved; private double oilBBLMoved; private double gasMCFMoved; private long strokeStartTime; Card(long strokeNumber){ this.strokeNumber = strokeNumber; strokeStartTime = System.currentTimeMillis(); } public void setSurfacePosition(int i, double position){ this.surfacePosition[i] = position; }; public void setDownholePosition(int i, double position){ this.downholePosition[i] = position; }; public void setSurfaceLoad(int i, double load){ this.surfaceLoad[i] = load; }; public void setDownholeLoad(int i, double load){ this.downholeLoad[i] = load; }; public double[] getSurfacePosition() { return this.surfacePosition; } public double[] getDownholePosition() { return this.downholePosition; } public double[] getSurfaceLoad() { return this.surfaceLoad; } public double[] getDownholeLoad() { return this.downholeLoad; } public LPPair getSurfacePositionMax() { return this.surfacePositionMax; } public long getStrokeNumber() { return strokeNumber; } public double getSurfaceStrokeLength() { return surfaceStrokeLength; } public double getDownholeNetStrokeLength() { return downholeNetStrokeLength; } public double getDownholeGrossStrokeLength() { return downholeGrossStrokeLength; } public double getDownholeAdjustedGrossStrokeLength() { return downholeAdjustedGrossStrokeLength; } public double getDownholeLoadSpan() { return downholeLoadSpan; } public double getFluidLoad() { return fluidLoad; } public double getFillageEstimated() { return fillageEstimated; } public double getFillageCalculated() { return fillageCalculated; } public double getTubingMovement() { return tubingMovement; } public double getStructuralLoading() { return structuralLoading; } public double getPumpIntakePressure() { return pumpIntakePressure; } public double getFluidLevel() { return fluidLevel; } public double getStrokeSpeed() { return strokeSpeed; } public double getPolishedRodHorsepower() { return polishedRodHorsepower; } public double getPumpHorsepower() { return pumpHorsepower; } public LPPair getSurfaceLoadMax() { return surfaceLoadMax; } public LPPair getSurfaceLoadMin() { return surfaceLoadMin; } public LPPair getDownholeLoadMax() { return downholeLoadMax; } public LPPair getDownholeLoadMin() { return downholeLoadMin; } public double getFluidBBLMoved() { return fluidBBLMoved; } public double getFluidBBLMovedAdjusted() { return fluidBBLMovedAdjusted; } public double getWaterBBLMoved() { return waterBBLMoved; } public double getOilBBLMoved() { return oilBBLMoved; } public double getGasMCFMoved() { return gasMCFMoved; } public int getNumPointsUsed() { return numPointsUsed; } public void setNumPointsUsed(int numPointsUsed) { this.numPointsUsed = numPointsUsed; } private void calculateSPM(){ long now = System.currentTimeMillis(); long strokeMillis = now - strokeStartTime; strokeSpeed = 60000.0 / (double)(now - strokeStartTime); } private static double lineResolve(double x1, double x2, double y1, double y2, double xTest) { double line_m = (y2 - y1) / (x2 - x1); double line_b = y1 - line_m * x1; double yTest = line_m * xTest + line_b; return yTest; } private static LPPair positionMax(double[] positionArr, double[] loadArr, int arrSize) { double maxPos = positionArr[0]; double loadAtMaxP = Double.MIN_VALUE; for(int i = 0; i < arrSize; i++) { maxPos = Math.max(maxPos, positionArr[i]); if (maxPos == positionArr[i]) loadAtMaxP = loadArr[i]; } return new LPPair(maxPos, loadAtMaxP); } private static LPPair positionMin(double[] positionArr, double[] loadArr, int arrSize) { double minPos = positionArr[0]; double loadAtMinP = Double.MAX_VALUE; for(int i = 0; i < arrSize; i++) { minPos = Math.min(minPos, positionArr[i]); if (minPos == positionArr[i]) loadAtMinP = loadArr[i]; } return new LPPair(minPos, loadAtMinP); } private static LPPair loadMax(double[] positionArr, double[] loadArr, int arrSize) { double maxLoad = loadArr[0]; double posAtMaxL = Double.MIN_VALUE; for(int i = 0; i < arrSize; i++) { maxLoad = Math.max(maxLoad, loadArr[i]); if (maxLoad == positionArr[i]) posAtMaxL = positionArr[i]; } return new LPPair(posAtMaxL, maxLoad); } private static LPPair loadMin(double[] positionArr, double[] loadArr, int arrSize) { double minLoad = loadArr[0]; double posAtMinL = Double.MAX_VALUE; for(int i = 0; i < arrSize; i++) { minLoad = Math.min(minLoad, loadArr[i]); if (minLoad == positionArr[i]) posAtMinL = positionArr[i]; } return new LPPair(posAtMinL, minLoad); } private double distanceToLine(double pos, double load) { double x1 = downholePositionMin.getPosition(); double x2 = downholePositionMax.getPosition(); double y1 = downholeLoadMin.getLoad(); double y2 = downholeLoadMax.getLoad(); return abs((y2-y1)*pos - (x2-x1)*load + x2*y1 - y2*x1) / sqrt(pow(y2-y1, 2) + pow(x2-x1,2)); }; void calcStrokeData(int numSlices, double fluidGradient, double rodDepth, double anchorDepth, double tubingCSA, double pumpArea, double frictionEstimate, double structuralRating, double kFactor, double waterBBLRatio, double oilBBLRatio, double gasMCFRatio) { calculateSPM(); surfacePositionMax = positionMax(surfacePosition, surfaceLoad, numPointsUsed); surfaceLoadMax = loadMax(surfacePosition, surfaceLoad, numPointsUsed); surfacePositionMin = positionMin(surfacePosition, surfaceLoad, numPointsUsed); surfaceLoadMin = loadMin(surfacePosition, surfaceLoad, numPointsUsed); downholePositionMax = positionMax(downholePosition, downholeLoad, numPointsUsed); downholeLoadMax = loadMax(downholePosition, downholeLoad, numPointsUsed); downholePositionMin = positionMin(downholePosition, downholeLoad, numPointsUsed); downholeLoadMin = loadMin(downholePosition, downholeLoad, numPointsUsed); surfaceStrokeLength = surfacePositionMax.getPosition() - surfacePositionMin.getPosition(); downholeGrossStrokeLength = downholePositionMax.getPosition() - downholePositionMin.getPosition(); downholeLoadSpan = downholeLoadMax.getLoad() - downholeLoadMin.getLoad(); double dxSurf = (surfacePositionMax.getPosition() - surfacePositionMin.getPosition()) / (float) numSlices; double dxDown = (downholePositionMax.getPosition() - downholePositionMin.getPosition()) / (float) numSlices; pumpHorsepower = 0.0; polishedRodHorsepower = 0.0; double dhDistanceTop = 0.0; double dhDistanceBottom = 0.0; for (int i = 1; i < numSlices+1; i++) { double suPosTarget = surfacePositionMin.getPosition() + ((double) i * dxSurf); double dhPosTarget = downholePositionMin.getPosition() + ((double) i * dxDown); double suLoadAtTargetTop = 0.0; double suLoadAtTargetBottom = 0.0; double dhLoadAtTargetTop = 0.0; double dhLoadAtTargetBottom = 0.0; for(int j = 0; j < numPointsUsed - 1; j++) { if (downholePosition[j] <= dhPosTarget && downholePosition[j+1] > dhPosTarget){ dhLoadAtTargetTop = lineResolve(downholePosition[j], downholePosition[j+1], downholeLoad[j], downholeLoad[j+1], dhPosTarget); } if (downholePosition[j] > dhPosTarget && downholePosition[j+1] >= dhPosTarget){ dhLoadAtTargetBottom = lineResolve(downholePosition[j], downholePosition[j+1], downholeLoad[j], downholeLoad[j+1], dhPosTarget); } if (surfacePosition[j] <= suPosTarget && surfacePosition[j+1] > suPosTarget){ suLoadAtTargetTop = lineResolve(surfacePosition[j], surfacePosition[j+1], surfaceLoad[j], surfaceLoad[j+1], suPosTarget); } if (surfacePosition[j] > suPosTarget && surfacePosition[j+1] >= suPosTarget){ suLoadAtTargetBottom = lineResolve(surfacePosition[j], surfacePosition[j+1], surfaceLoad[j], surfaceLoad[j+1], suPosTarget); } } polishedRodHorsepower += (dxSurf / 12.0) * (suLoadAtTargetTop - suLoadAtTargetBottom) * (strokeSpeed / 33000.0); pumpHorsepower += (dxDown / 12.0) * (dhLoadAtTargetTop - dhLoadAtTargetBottom) * (strokeSpeed / 33000.0); double tDistance = distanceToLine(dhPosTarget, dhLoadAtTargetTop); double bDistance = distanceToLine(dhPosTarget, dhLoadAtTargetBottom); if (tDistance > dhDistanceTop) { dhDistanceTop = tDistance; topCorner = new LPPair(dhPosTarget, dhLoadAtTargetTop); } if (bDistance > dhDistanceBottom) { dhDistanceBottom = bDistance; bottomCorner = new LPPair(dhPosTarget, dhLoadAtTargetBottom); } } downholeAdjustedGrossStrokeLength = downholePositionMax.getPosition() - topCorner.getPosition(); downholeNetStrokeLength = bottomCorner.getPosition() - downholePositionMin.getPosition(); fillageCalculated = (downholeNetStrokeLength / downholeAdjustedGrossStrokeLength) * 100.0; fillageEstimated =(downholeNetStrokeLength / downholeGrossStrokeLength) * 100.0; fluidBBLMoved = downholeNetStrokeLength * pumpArea * 0.00010307; fluidBBLMovedAdjusted = fluidBBLMoved * kFactor; oilBBLMoved = fluidBBLMoved * oilBBLRatio; waterBBLMoved = fluidBBLMoved * waterBBLRatio; gasMCFMoved = fluidBBLMoved * gasMCFRatio; if (fillageEstimated > 100) fillageEstimated = 100.0; if (fillageCalculated > 100) fillageCalculated = 100.0; fluidLoad = downholeLoadSpan - frictionEstimate; pumpIntakePressure = fluidGradient * rodDepth - (fluidLoad / pumpArea); //printf("PIP = %f * %f - (%f / %f) = %f\n", fluidGradient, rodDepth, fluidLoad, pumpArea, pumpIntakePressure); fluidLevel = pumpIntakePressure / fluidGradient; tubingMovement = 12 * (rodDepth - anchorDepth) * fluidLoad / (30500000 * tubingCSA); structuralLoading = (surfaceLoadMax.getLoad() / (structuralRating * 100)) * 100; }; public void printCard(String printType, boolean printDataTableToo){ if(printType.toLowerCase().equals("table")) { V2_AsciiTable pointsTable = new V2_AsciiTable(); pointsTable.addRule(); pointsTable.addRow("Surface Position", "Surface Load", "Downhole Position", "Downhole Load"); pointsTable.addRule(); for (int i = 0; i < numPointsUsed; i++) { pointsTable.addRow(surfacePosition[i], surfaceLoad[i], downholePosition[i], downholeLoad[i]); } pointsTable.addRule(); V2_AsciiTableRenderer rend = new V2_AsciiTableRenderer(); rend.setTheme(V2_E_TableThemes.UTF_LIGHT.get()); rend.setWidth(new WidthAbsoluteEven(100)); RenderedTable rt = rend.render(pointsTable); System.out.println(rt); System.out.println(); } else if (printType.toLowerCase().equals("csv")){ System.out.println("surf_pos,surf_load,down_pos,down_load"); for (int i = 0; i < numPointsUsed; i++) { System.out.printf("%f,%f,%f,%f\n", surfacePosition[i], surfaceLoad[i], downholePosition[i], downholeLoad[i]); } System.out.println(); } if(printDataTableToo){ V2_AsciiTable dataTable = new V2_AsciiTable(); dataTable.addRule(); dataTable.addRow("Card " + strokeNumber, "Value"); dataTable.addStrongRule(); dataTable.addRow("Fill % (Est.)", fillageEstimated); dataTable.addRow("Fill % (Calc.)", fillageCalculated); dataTable.addRule(); dataTable.addRow("Surf. Stroke Length", surfaceStrokeLength); dataTable.addRow("Down. Gross Stroke", downholeGrossStrokeLength); dataTable.addRow("Down. Net Stroke", downholeNetStrokeLength); dataTable.addRow("Tubing Movement", tubingMovement); dataTable.addRule(); dataTable.addRow("Polished Rod HP", polishedRodHorsepower); dataTable.addRow("Pump HP", pumpHorsepower); dataTable.addRule(); dataTable.addRow("Stroke Speed", strokeSpeed); dataTable.addRule(); dataTable.addRow("Fluid Load", fluidLoad); dataTable.addRow("Pump Intake Pressure", pumpIntakePressure); dataTable.addRow("Fluid Level", fluidLevel); dataTable.addRule(); dataTable.addRow("Structural Loading", structuralLoading); dataTable.addRule(); V2_AsciiTableRenderer rend = new V2_AsciiTableRenderer(); rend.setTheme(V2_E_TableThemes.UTF_LIGHT.get()); rend.setWidth(new WidthAbsoluteEven(50)); RenderedTable rt = rend.render(dataTable); System.out.println(rt); System.out.println(); } } }