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 org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import static java.lang.Math.exp; import static java.lang.Math.pow; import static java.lang.Math.sqrt; import java.io.FileReader; import java.io.IOException; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; /** * Created by patrickjmcd on 1/31/17. */ public class Well { private String wellName; protected Simulation sim; protected Database db; protected WellTest wellTest; /* IO */ AnalogIn inclinometer; AnalogIn loadCell; private double currentSurfacePosition; private double currentSurfaceLoad; private double currentDownholePosition; private double currentDownholeLoad; /* CARDS */ private Card currentCard; public Card[] cardStorage = new Card[100]; // CONSTANTS private static double YM_STEEL = 30.5; private static double YM_FIBERGLASS = 7.2; static final int BAD_STATUS = 0; static final int GOOD_STATUS = 1; /* USER INPUTS */ private double dt; private double tubingHeadPressure; private double fluidGradient; private double sbfriction; private int numTapers; private double tubingAnchorDepth; private double pumpDiameter; private double tubingID, tubingOD; private double structuralRating; private double[] c = new double[11]; private double[] rodLength = new double[11]; private double[] rodDiameter = new double[11]; private double[] rodYM = new double[11]; private double[] rodWeightPerFoot = new double[11]; // CALCULATED TAPER PARAMETERS private int nT1; // should always be equal to number of tapers + 1 private double frictionEstimate; private double theoreticalMaxFluidLoad; private double[] a = new double[11]; private double[] area = new double[11]; private double[] pressure = new double[11]; private double[] buoyantForce = new double[11]; private double buoyantForceTotal; private double[] stretch = new double[11]; private double[] weightData = new double[11]; private double weightDataTotal; private double[] annularForceData = new double[11]; private double annularForceDataTotal; private double[] force = new double[11]; private double[] alpha = new double[11]; private double[] xOverA = new double[11]; private double[] factor = new double[11]; private int[] lagIndex = new int[11]; private int[] lengthRequired = new int[11]; private int[] centerPoint = new int[11]; private double[] rodDepth = new double[11]; private double rodDepthTotal; private double[] rodWeightAir = new double[11]; private double rodWeightAirTotal; private double[] rodWeightFluid = new double[11]; private double rodWeightFluidTotal; private double pumpArea; private double tubingCrossSectionalArea; // Statuses private volatile int runStatus; public static final int RUNSTATUS_STOPPED = 0; public static final int RUNSTATUS_STARTING = 1; public static final int RUNSTATUS_RUNNING = 2; public static final int RUNSTATUS_PUMPEDOFF = 3; public static final int RUNSTATUS_FAULTED = 4; public static final int RUNSTATUS_LOCKEDOUT = 5; private boolean permissiveOK; private long strokesSinceStart = 0; private long startupStrokes = 1; private long strokesToday = 0; private long strokesLifetime; private int pointCounter =0; // DIRECTION private static final int DIRECTION_UNKNOWN = 0; private static final int DIRECTION_UP = 1; private static final int DIRECTION_DOWN = 2; private int direction = DIRECTION_UNKNOWN; private int lastDirection = DIRECTION_UNKNOWN; // Modes private volatile int runMode; public static final int RUNMODE_POC = 0; public static final int RUNMODE_MANUAL = 1; public static final int RUNMODE_TIMER = 2; // Intermediate Variables private double[][] topPosArray = new double[10][100]; private double[][] topLoadArray = new double[10][100]; private double loadBefore = 0.0; private double loadAfter = 0.0; private double loadBefore3 = 0.0; private double loadAfter3 = 0.0; private int[] count = new int[11]; private double sPositionPrevious; // Fluid Makeup private double fluidOilRatio; // BBL of oil per 1 BBL fluid private double fluidWaterRatio; // BBL of water per 1 BBL fluid private double fluidGasRatio; // MCF of gas per 1 BBL fluid private double kFactor = 1.0; // DATE & TIME PARAMETERS private ZonedDateTime now = ZonedDateTime.now(); private boolean isNewDay(){ ZonedDateTime today = ZonedDateTime.now(); boolean ret = !(today.toLocalDate().equals(now.toLocalDate())); now = today; return ret; } // Measurements private Measurement strokeSpeed; private Measurement downholeGrossStroke; private Measurement downholeNetStroke; private Measurement fluidLevel; private Measurement fluidLoad; private Measurement inflowRate; private Measurement peakPolishedRodLoad; private Measurement minPolishedRodLoad; private Measurement percentRun; private Measurement polishedRodHP; private Measurement pumpHP; private Measurement fluidProduced; private Measurement fluidProducedAdjusted; private Measurement oilProduced; private Measurement waterProduced; private Measurement gasProduced; private Measurement pumpIntakePressure; private Measurement surfaceStrokeLength; private Measurement tubingMovement; private Measurement pumpFillPercent; // Running Setpoints private double pumpOffFillPercentSetpoint = 65; private int pumpOffStrokes = 5; private int lowFillageStrokes = 0; private ZonedDateTime pumpedOffTime; private long pumpOffDowntimeMinutes = 5; private long minutesSincePumpOff = 0; private long minutesSincePumpOff_last = 0; Well(String dbHostname, int inclinometerChannel, int loadCellChannel, int runCommandChannel){ this.wellName = wellName; db = new Database(dbHostname); strokesLifetime = db.getLastStrokeNum() + 1; currentCard = new Card(strokesLifetime); inclinometer = new AnalogIn(inclinometerChannel, 0, 100, 0, 100); loadCell = new AnalogIn(loadCellChannel, 0, 50000, 0, 50000); initializeMeasurements(); db.newRunStatus("Boot", "io"); } Well(String dbHostname, String simFileName, int inclinometerChannel, int loadCellChannel, int runCommandChannel){ this.wellName = wellName; sim = new Simulation(simFileName); db = new Database(dbHostname); strokesLifetime = db.getLastStrokeNum() + 1; currentCard = new Card(strokesLifetime); inclinometer = new AnalogIn(inclinometerChannel, 0, 100, 0, 100); loadCell = new AnalogIn(loadCellChannel, 0, 50000, 0, 50000); initializeMeasurements(); db.newRunStatus("Boot", "commandline"); } private void initializeMeasurements(){ strokeSpeed = new Measurement("Stroke Speed", db, 0.5, 600); downholeGrossStroke = new Measurement("Downhole Gross Stroke", db, 0.5, 600); downholeNetStroke = new Measurement("Downhole Net Stroke", db, 0.5, 600); fluidLevel = new Measurement("Fluid Level", db, 10, 600); fluidLoad = new Measurement("Fluid Load", db, 20, 600); inflowRate = new Measurement("Inflow Rate", db, 0.5, 600); peakPolishedRodLoad = new Measurement("Peak PRL", db, 50, 600); minPolishedRodLoad = new Measurement("Min PRL", db, 50, 600); percentRun = new Measurement("Percent Run", db, 1.0, 600); polishedRodHP = new Measurement("Polished Rod HP", db, 0.25, 600); pumpHP = new Measurement("Pump HP", db, 0.25, 600); fluidProduced = new Measurement("Fluid Produced", db, 1.0, 600); fluidProducedAdjusted = new Measurement("Fluid Produced (adjusted)", db, 1.0, 600); oilProduced = new Measurement("Oil Produced", db, 1.0, 600); waterProduced = new Measurement("Water Produced", db, 1.0, 600); gasProduced = new Measurement("Gas Produced", db, 1.0, 600); pumpIntakePressure = new Measurement("Pump Intake Pressure", db, 5.0, 600); surfaceStrokeLength = new Measurement("Surface Stroke", db, 0.5, 1800); tubingMovement = new Measurement("Tubing Movement", db, 0.5, 600); pumpFillPercent = new Measurement("Pump Fill Percent", db, 0.5, 600); } public double getDt() { return dt; } public void setDt(double dt) { this.dt = dt; } public double getTubingHeadPressure() { return tubingHeadPressure; } public void setTubingHeadPressure(double tubingHeadPressure) { this.tubingHeadPressure = tubingHeadPressure; } public double getFluidGradient() { return fluidGradient; } public void setFluidGradient(double fluidGradient) { this.fluidGradient = fluidGradient; } public double getSbfriction() { return sbfriction; } public void setSbfriction(double sbfriction) { this.sbfriction = sbfriction; } public int getNumTapers() { return numTapers; } public void setNumTapers(int numTapers) { this.numTapers = numTapers; } public double getTubingAnchorDepth() { return tubingAnchorDepth; } public void setTubingAnchorDepth(double tubingAnchorDepth) { this.tubingAnchorDepth = tubingAnchorDepth; } public double getPumpDiameter() { return pumpDiameter; } public void setPumpDiameter(double pumpDiameter) { this.pumpDiameter = pumpDiameter; this.pumpArea = pow(pumpDiameter, 2) * Math.PI; } public double getTubingID() { return tubingID; } public void setTubingID(double tubingID) { this.tubingID = tubingID; this.tubingCrossSectionalArea = (Math.PI / 4) * (pow(this.tubingOD, 2) - pow(this.tubingID,2)); } public double getTubingOD() { return tubingOD; } public void setTubingOD(double tubingOD) { this.tubingOD = tubingOD; this.tubingCrossSectionalArea = (Math.PI / 4) * (pow(this.tubingOD, 2) - pow(tubingID,2)); } public void setDampingFactor(int i, double c) { this.c[i] = c; } public double getDampingFactor(int i){ return c[i]; } public void setRodLength(int i, double rodLength) { this.rodLength[i] = rodLength; } public double getRodLength(int i){ return rodLength[i]; } public void setRodDiameter(int i, double rodDiameter) { this.rodDiameter[i] = rodDiameter; } public double getRodDiameter(int i){ return rodDiameter[i]; } public void setRodYM(int i, String material) { if (material.toLowerCase().equals("steel")) rodYM[i] = YM_STEEL; else if (material.toLowerCase().equals("fiberglass")) rodYM[i] = YM_FIBERGLASS; } public double getRodYM(int i){ return rodYM[i]; } public String getRodMaterial(int i){ if(rodYM[i] == YM_STEEL) return "steel"; if(rodYM[i] == YM_FIBERGLASS) return "fiberglass"; return "unknown"; } public double getFrictionEstimate() { return frictionEstimate; } public void setFrictionEstimate(double frictionEstimate) { this.frictionEstimate = frictionEstimate; } public double getRodDepthTotal() { return rodDepthTotal; } public double getPumpArea() { return pumpArea; } public double getTubingCrossSectionalArea() { return tubingCrossSectionalArea; } public double getStructuralRating() { return structuralRating; } public void setStructuralRating(double structuralRating) { this.structuralRating = structuralRating; } public void setWellName(String wellName) { this.wellName = wellName; } public String getWellName() { return wellName; } public boolean isPermissiveOK() { return permissiveOK; } public int getRunStatus() { return runStatus; } public String getRunStatusString(){ switch(runStatus){ case RUNSTATUS_STOPPED: return "Stopped"; case RUNSTATUS_STARTING: return "Starting"; case RUNSTATUS_RUNNING: return "Running"; case RUNSTATUS_PUMPEDOFF: return "Pumped-Off"; case RUNSTATUS_FAULTED: return "Faulted"; case RUNSTATUS_LOCKEDOUT: return "Locked Out"; default: return "Unknown"; } } public int getRunMode() { return runMode; } public double getCurrentSurfacePosition() { return currentSurfacePosition; } public double getCurrentSurfaceLoad() { return currentSurfaceLoad; } public double getCurrentDownholePosition() { return currentDownholePosition; } public double getCurrentDownholeLoad() { return currentDownholeLoad; } public long getStartupStrokes() { return startupStrokes; } public void setStartupStrokes(long startupStrokes) { this.startupStrokes = startupStrokes; } public long getStrokesSinceStart() { return strokesSinceStart; } public long getStrokesToday() { return strokesToday; } public long getStrokesLifetime() { return strokesLifetime; } public int getDirection() { return direction; } public double getFluidOilRatio() { return fluidOilRatio; } public double getFluidWaterRatio() { return fluidWaterRatio; } public double getFluidGasRatio() { return fluidGasRatio; } public double getkFactor() { return kFactor; } public void setkFactor(double kFactor) { this.kFactor = kFactor; } public int getLowFillageStrokes() { return lowFillageStrokes; } public ZonedDateTime getPumpedOffTime() { return pumpedOffTime; } public long getMinutesSincePumpOff() { return minutesSincePumpOff; } public double getPumpOffFillPercentSetpoint() { return pumpOffFillPercentSetpoint; } public void setPumpOffFillPercentSetpoint(double pumpOffFillPercentSetpoint) { this.pumpOffFillPercentSetpoint = pumpOffFillPercentSetpoint; } public int getPumpOffStrokes() { return pumpOffStrokes; } public void setPumpOffStrokes(int pumpOffStrokes) { this.pumpOffStrokes = pumpOffStrokes; } public long getPumpOffDowntimeMinutes() { return pumpOffDowntimeMinutes; } public void setPumpOffDowntimeMinutes(long pumpOffDowntimeMinutes) { this.pumpOffDowntimeMinutes = pumpOffDowntimeMinutes; } public void setupFluidRatio(double oilRatio, double waterRatio, double gasRatio){ fluidOilRatio = oilRatio; fluidWaterRatio = waterRatio; fluidGasRatio = gasRatio; } // WELL COMMAND FUNCTIONS public void start(String initiator){ if (runStatus == RUNSTATUS_STOPPED && permissiveOK){ System.out.println("Starting " + wellName + " from " + initiator + "..."); runStatus = RUNSTATUS_STARTING; strokesSinceStart = 0; db.newRunStatus(getRunStatusString(), initiator); } } public void stop(String initiator){ if (runStatus == RUNSTATUS_STARTING || runStatus == RUNSTATUS_RUNNING || runStatus == RUNSTATUS_PUMPEDOFF){ System.out.println("Stopping " + wellName + " from " + initiator + "..."); runStatus = RUNSTATUS_STOPPED; db.newRunStatus(getRunStatusString(), initiator); } } public void restart(String initiator){ if (runStatus == RUNSTATUS_PUMPEDOFF && permissiveOK){ System.out.println("Restarting after pump-off from " + initiator + "..."); runStatus = RUNSTATUS_STARTING; strokesSinceStart = 0; db.newRunStatus(getRunStatusString(), initiator); } } public void pumpOff(String initiator){ if (runStatus == RUNSTATUS_RUNNING){ System.out.println("Pumping off from " + initiator + "..."); System.out.println("Restarting in " + pumpOffDowntimeMinutes + " minutes."); pumpedOffTime = ZonedDateTime.now(); runStatus = RUNSTATUS_PUMPEDOFF; db.newRunStatus(getRunStatusString(), initiator); } } public void checkSafeties(){ permissiveOK = true; } // WELL CALCULATION FUNCTIONS private static double lookupRodWeightPerFoot(double i_ym, double i_diam) { double wtPerFt; if (i_ym == YM_STEEL) { 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 == YM_FIBERGLASS) { 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; }; void parseJSONFile(String jsonFilename){ JSONParser parser = new JSONParser(); try { Object obj = parser.parse(new FileReader(jsonFilename)); JSONObject well = (JSONObject) obj; Object newWellName = well.get("wellName"); if (newWellName != null) wellName = (String) newWellName; Object newDeltaT = well.get("deltaT"); if (newDeltaT != null) dt = (Double) newDeltaT; Object newPumpDiameter = well.get("pumpDiameter"); if (newPumpDiameter != null) setPumpDiameter((Double) newPumpDiameter); Object newFluidGradient = well.get("fluidGradient"); if (newFluidGradient != null) fluidGradient = (Double) newFluidGradient; Object newTubingID = well.get("tubingID"); if (newTubingID != null) setTubingID((Double) newTubingID); Object newTubingOD = well.get("tubingOD"); if (newTubingOD != null) setTubingOD((Double) newTubingOD); Object newTubingAnchorDepth = well.get("tubingAnchorDepth"); if (newTubingAnchorDepth != null) tubingAnchorDepth = (Double) newTubingAnchorDepth; Object newStructuralRating = well.get("structuralRating"); if (newStructuralRating != null) structuralRating = (Double) newStructuralRating; Object newStuffingBoxFriction = well.get("stuffingBoxFriction"); if (newStuffingBoxFriction != null) setSbfriction((Double) newStuffingBoxFriction); Object newTubingHeadPressure = well.get("tubingOD"); if (newTubingHeadPressure != null) setTubingOD((Double) newTubingHeadPressure); JSONArray tapers = (JSONArray) well.get("tapers"); numTapers = tapers.size(); for (int i = 0; i < numTapers; i++){ int currentTaperNum = i + 1; JSONObject taperObj = (JSONObject) tapers.get(i); Object newLength = taperObj.get("length"); if (newLength != null) setRodLength(currentTaperNum, (Double) newLength); Object newDiameter = taperObj.get("diameter"); if (newDiameter != null) setRodDiameter(currentTaperNum, (Double) newDiameter); Object newMaterial = taperObj.get("material"); if (newMaterial != null) setRodYM(currentTaperNum, (String) newMaterial); Object newDampingFactor = taperObj.get("dampingFactor"); if (newDampingFactor != null) setDampingFactor(currentTaperNum, (Double) newDampingFactor); } } catch (IOException | ParseException e) { e.printStackTrace(); } updateTapers(); } void printTapers(){ System.out.println("===== " + wellName + " ====="); System.out.println("--- INPUT PARAMETERS ---"); V2_AsciiTable inputTable = new V2_AsciiTable(); inputTable.addRule(); inputTable.addRow("DeltaT", getDt()); inputTable.addStrongRule(); inputTable.addRow("Fluid Gradient", getFluidGradient()); inputTable.addRow("Pump Diameter", getPumpDiameter()); inputTable.addRow("Stuffing Box Friction", getSbfriction()); inputTable.addRow("Friction Estimate", getFrictionEstimate()); inputTable.addRule(); inputTable.addRow("Tubing Anchor Depth", getTubingAnchorDepth()); inputTable.addRow("Tubing ID", getTubingID()); inputTable.addRow("Tubing OD", getTubingOD()); inputTable.addRule(); inputTable.addRow("Number of Tapers", getNumTapers()); inputTable.addRule(); inputTable.addRow("Buoyant Force Total", buoyantForceTotal); inputTable.addRow("Weight Data Total", weightDataTotal); inputTable.addRow("Annular Force Data Total", annularForceDataTotal); inputTable.addRow("Rod Depth Total", rodDepthTotal); inputTable.addRow("Rod Weight Air Total", rodWeightAirTotal); inputTable.addRow("Rod Weight Fluid Total", rodWeightFluidTotal); inputTable.addRule(); V2_AsciiTableRenderer rend = new V2_AsciiTableRenderer(); rend.setTheme(V2_E_TableThemes.UTF_LIGHT.get()); rend.setWidth(new WidthAbsoluteEven(50)); RenderedTable rt = rend.render(inputTable); System.out.println(rt); System.out.println(); for(int i = 1; i <= numTapers; i++){ V2_AsciiTable taperTable = new V2_AsciiTable(); taperTable.addRule(); taperTable.addRow("Taper", i); taperTable.addStrongRule(); taperTable.addRow("Rod Length", getRodLength(i)); taperTable.addRow("Rod Diameter", getRodDiameter(i)); taperTable.addRow("Rod Damping Factor", getDampingFactor(i)); taperTable.addRow("Rod Material", getRodMaterial(i)); taperTable.addRow("Rod Young's Modulus", getRodYM(i)); taperTable.addRule(); taperTable.addRow("a", a[i]); taperTable.addRow("Area", area[i]); taperTable.addRow("Pressure", pressure[i]); taperTable.addRow("Buoyant Force", buoyantForce[i]); taperTable.addRow("Stretch", stretch[i]); taperTable.addRow("WeightData", weightData[i]); taperTable.addRow("Annular Force Data", annularForceData[i]); taperTable.addRow("force", force[i]); taperTable.addRow("Alpha", alpha[i]); taperTable.addRow("xOverA", xOverA[i]); taperTable.addRow("Factor", factor[i]); taperTable.addRow("Lag Index", lagIndex[i]); taperTable.addRow("Length Required", lengthRequired[i]); taperTable.addRow("Center Point", centerPoint[i]); taperTable.addRow("Rod Depth", rodDepth[i]); taperTable.addRow("Rod Weight in Air", rodWeightAir[i]); taperTable.addRow("Rod Weight in Fluid", rodWeightFluid[i]); taperTable.addRule(); rend.setTheme(V2_E_TableThemes.UTF_LIGHT.get()); rend.setWidth(new WidthAbsoluteEven(50)); rt = rend.render(taperTable); System.out.println(rt); System.out.println(); } }; private void updateTapers(){ nT1 = numTapers + 1; // start by setting everything to 0 a[0] = 0.0; area[0] = 0.0; area[numTapers + 1] = 0.0; pressure[0] = tubingHeadPressure; buoyantForce[0] = 0.0; stretch[0] = 0.0; weightData[0] = 0.0; annularForceData[0] = 0.0; force[0] = 0.0; alpha[0] = 0.0; xOverA[0] = 0.0; factor[0] = 0.0; lagIndex[0] = 0; lengthRequired[0] = 0; centerPoint[0] = 0; rodDepth[0] = 0.0; rodWeightAir[0] = 0.0; rodWeightFluid[0] = 0.0; buoyantForceTotal = 0.0; weightDataTotal = 0.0; annularForceDataTotal = 0.0; rodDepthTotal = 0.0; rodWeightAirTotal = 0.0; rodWeightFluidTotal = 0.0; for(int i = 1; i < nT1; i++){ rodWeightPerFoot[i] = lookupRodWeightPerFoot(rodYM[i], rodDiameter[i]); } for (int area_i = 1; area_i < nT1; area_i++) { area[area_i] = (Math.PI / 4) * pow(rodDiameter[area_i], 2); } for (int i = 1; i < nT1; i++) { a[i] = 1000 * sqrt(32.2 * rodYM[i] * area[i] / rodWeightPerFoot[i]); rodDepth[i] = rodDepth[i - 1] + rodLength[i]; pressure[i] = pressure[i - 1] + fluidGradient * rodLength[i]; buoyantForce[i] = pressure[i] * (area[i + 1] - area[i]); rodWeightAir[i] = rodWeightPerFoot[i] * rodLength[i]; rodWeightFluid[i] = rodWeightAir[i] + buoyantForce[i]; } for (int j = 1; j < nT1; j++) { for (int k = j + 1; k < nT1; k++) { weightData[j] += rodWeightAir[k]; // how much weight is felt on each taper } for (int l = j; l < numTapers; l++) { annularForceData[j] += -buoyantForce[l]; //buoyant force exerted on each taper } force[j] = (-area[numTapers] * pressure[numTapers]) + weightData[j] - annularForceData[j]; alpha[j] = (force[j] + rodWeightAir[j]) / (rodYM[j] * pow(10, 6) * area[j]); stretch[j] = (stretch[j - 1] + (alpha[j] * rodLength[j])) - ((rodWeightPerFoot[j] * pow(rodLength[j], 2)) / (2 * rodYM[j] * pow(10, 6) * area[j])); } for (int m = 1; m < nT1; m++) { xOverA[m] = rodLength[m] / a[m]; lagIndex[m] = (int)(rodLength[m] / (a[m] * dt)); factor[m] = (xOverA[m] - lagIndex[m] * dt) / dt; centerPoint[m] = lagIndex[m] + 2; lengthRequired[m] = 2 * (lagIndex[m] + 1) + 1; } for (int t = 0; t < nT1; t++) { buoyantForceTotal += buoyantForce[t]; rodWeightAirTotal += rodWeightAir[t]; rodWeightFluidTotal += rodWeightFluid[t]; rodDepthTotal += rodLength[t]; annularForceDataTotal += annularForceData[t]; weightDataTotal += weightData[t]; } theoreticalMaxFluidLoad = fluidGradient* rodDepthTotal* pumpArea; frictionEstimate = 0.10 * rodDepthTotal; double dbFrictionEstimate = db.getLatestFrictionEstimate(); if (dbFrictionEstimate != -1){ frictionEstimate = dbFrictionEstimate; } } private double position(int p) //generate the downhole position of the given rod part { loadBefore = 0; loadAfter = 0; loadBefore3 = 0; loadAfter3 = 0; //Temporary Variables double a1 = a[p]; double cd = c[p]; double fact = factor[p]; double rodLengthp = rodLength[p]; int lagInd = lagIndex[p]; double Y1 = rodYM[p] * pow(10, 6); double A1 = area[p]; int centerOfArray = centerPoint[p]; int iBefore = centerOfArray - lagInd; int iAfter = centerOfArray + lagInd; double pumpPOS = 0; //Do work pumpPOS = exp((cd * rodLengthp) / (2 * a1)) * (topPosArray[p][iAfter] + fact * (topPosArray[p][ iAfter + 1] - topPosArray[p][iAfter])); pumpPOS += exp(-(cd * rodLengthp) / (2 * a1)) * (topPosArray[p][iBefore] + fact * (topPosArray[p][iBefore - 1] - topPosArray[p][iBefore])); pumpPOS = pumpPOS / 2; double insideIntegral = 0; int q = 1; while (q < 2 * lagInd - 1) { insideIntegral += dt * (1 / (Y1 * A1)) * (exp(-(cd * (lagInd - q) * dt) / 2) * topLoadArray[p][iBefore + q]); q++; } insideIntegral += 0.5 * dt * (1 / (Y1 * A1)) * (exp(-(cd * lagInd * dt) / 2) * topLoadArray[p][iBefore] + exp(-(cd * (-lagInd) * dt) / 2) * topLoadArray[p][iAfter]); loadBefore = exp(-(cd * lagInd * dt) / 2) * topLoadArray[p][iBefore] + fact * (exp(-(cd * (lagInd + 1) * dt) / 2) * topLoadArray[p][iBefore - 1] - exp(-(cd * lagInd * dt) / 2) * topLoadArray[p][iBefore]); loadAfter = exp(-(cd * (-lagInd) * dt) / 2) * topLoadArray[p][iAfter] + fact * (exp(-(cd * (-lagInd - 1) * dt) / 2) * topLoadArray[p][iAfter + 1] - exp(-(cd * (-lagInd) * dt) / 2) * topLoadArray[p][iAfter]); insideIntegral += 0.5 * fact * dt * (1 / (Y1 * A1)) * (loadBefore + exp(-(cd * (lagInd) * dt) / 2) * topLoadArray[p][iBefore]); insideIntegral += 0.5 * fact * dt * (1 / (Y1 * A1)) * (loadAfter + exp(-(cd * (-lagInd) * dt) / 2) * topLoadArray[p][iAfter]); insideIntegral = 0.5 * a1 * insideIntegral; pumpPOS += insideIntegral; insideIntegral = 0; int r = 1; while (r < 2 * lagInd - 1) { insideIntegral += dt * (exp(-(cd * lagInd - r) * dt) / 2) * (topPosArray[p][iBefore + r]); r++; } insideIntegral += 0.5 * dt * (exp(-(cd * lagInd * dt) / 2) * topPosArray[p][iBefore] + exp(-(cd * (-lagInd) * dt) / 2) * topPosArray[p][iAfter]); loadBefore3 = exp(-(cd * (lagInd) * dt) / 2) * topPosArray[p][iBefore] + fact * (exp(-(cd * (lagInd + 1) * dt) / 2) * topPosArray[p][iBefore] - exp(-(cd * (lagInd) * dt) / 2) * topPosArray[p][iBefore]); loadAfter3 = exp(-(cd * (-lagInd) * dt) / 2) * topPosArray[p][iAfter] + fact * (exp(-(cd * (-lagInd - 1) * dt) / 2) * topPosArray[p][iAfter] - exp(-(cd * (-lagInd) * dt) / 2) * topPosArray[p][iAfter]); insideIntegral += 0.5 * fact * dt * (loadBefore3 + exp(-(cd * (lagInd) * dt) / 2) * topPosArray[p][iBefore]); insideIntegral += 0.5 * fact * dt * (loadAfter3 + exp(-(cd * (-lagInd) * dt) / 2) * topPosArray[p][iAfter]); insideIntegral = -((cd * rodLengthp) / 4) * (0.5 * (cd / (2 * a1))) * insideIntegral; pumpPOS += insideIntegral; //printf("Position: %f\n", pumpPOS); return pumpPOS; }; private double load(int s) //calculate the downhole load of the given rod part { //temporary variables double a1 = a[s]; double cd = c[s]; double rodLengths = rodLength[s]; int lagInd = lagIndex[s]; double Y1 = rodYM[s] * pow(10, 6); double A1 = area[s]; int centerOfArray = centerPoint[s]; int iBefore = centerOfArray - lagInd; int iAfter = centerOfArray + lagInd; double pumpLOAD = 0; pumpLOAD = 0.5 * (a1 / (Y1 * A1)) * (1 / a1) * (loadBefore + loadAfter); pumpLOAD += -((cd * rodLengths) / 4) * (0.5 * (cd / (2 * a1))) * (1 / a1) * (loadBefore3 + loadAfter3); double firstPart = 0; double pointAfter = (topPosArray[s][iAfter + 1] - topPosArray[s][iAfter - 1]) / (2 * dt); double pointBefore = (topPosArray[s][iBefore + 1] - topPosArray[s][iBefore - 1]) / (2 * dt); firstPart = (exp((cd * rodLengths) / (2 * a1)) * pointAfter - exp(-(cd * rodLengths) / (2 * a1)) * pointBefore) / (2 * a1); firstPart += (cd * exp((cd * rodLengths) / (2 * a1)) * topPosArray[s][iAfter] - cd * exp((-cd * rodLengths) / (2 * a1)) * topPosArray[s][iBefore]) / (4 * a1); pumpLOAD = Y1 * A1 * (firstPart + pumpLOAD); //printf("Load: %f\n", pumpLOAD); return pumpLOAD; }; private LPStatus calc(double sPosition, double sLoad){ boolean useShift = false; int loadMult = 1; int tapersAllowed = 1; double dPosition = 0; double dLoad = 0; int status = BAD_STATUS; for (int ii = 1; ii < lengthRequired[1] + 1; ii++) { topPosArray[1][ii - 1] = topPosArray[1][ii]; topLoadArray[1][ii - 1] = topLoadArray[1][ii]; } topPosArray[1][lengthRequired[1]] = -(sPosition / 12); //stores current position in feet if (sPosition > sPositionPrevious) { topLoadArray[1][lengthRequired[1]] = loadMult * (sLoad - rodWeightFluidTotal) - sbfriction; } else { topLoadArray[1][lengthRequired[1]] = loadMult * (sLoad - rodWeightFluidTotal) + sbfriction; } sPositionPrevious = sPosition; int j = 1; while (j <= tapersAllowed) { count[j]++; if (count[j] >= lengthRequired[j]) { if ((j + 1) <= numTapers) { for (int jj = 2; jj < lengthRequired[j + 1] + 1; jj++) { topPosArray[j + 1][jj - 1] = topPosArray[j + 1][jj]; topLoadArray[j + 1][jj - 1] = topLoadArray[j + 1][jj]; } topPosArray[j + 1][lengthRequired[j + 1]] = position(j); topLoadArray[j + 1][lengthRequired[j + 1]] = load(j); } else { if (useShift) { dPosition = -12 * (position(j) + stretch[numTapers]); } else { dPosition = -12 * position(j); } dLoad = load(j) + force[numTapers]; status = GOOD_STATUS; } count[j]--; tapersAllowed += 1; if (tapersAllowed > numTapers) { tapersAllowed = numTapers; } } j++; } LPStatus downholeValues = new LPStatus(dPosition, dLoad, status); return downholeValues; }; public void endOfStroke(){ currentCard.setNumPointsUsed(pointCounter + 1); currentCard.calcStrokeData(150, fluidGradient, rodDepthTotal, tubingAnchorDepth, tubingCrossSectionalArea, pumpArea, frictionEstimate, structuralRating, kFactor, fluidWaterRatio, fluidOilRatio, fluidGasRatio); System.arraycopy(cardStorage, 0, cardStorage, 1, cardStorage.length-1); cardStorage[0] = currentCard; currentCard.printCard("none", true); strokesSinceStart++; strokesToday++; strokesLifetime++; strokeSpeed.update(currentCard.getStrokeSpeed()); downholeGrossStroke.update(currentCard.getDownholeGrossStrokeLength()); downholeNetStroke.update(currentCard.getDownholeNetStrokeLength()); fluidLevel.update(currentCard.getFluidLevel()); fluidLoad.update(currentCard.getFluidLoad()); peakPolishedRodLoad.update(currentCard.getSurfaceLoadMax().getLoad()); minPolishedRodLoad.update(currentCard.getSurfaceLoadMin().getLoad()); polishedRodHP.update(currentCard.getPolishedRodHorsepower()); pumpHP.update(currentCard.getPumpHorsepower()); fluidProduced.update(currentCard.getFluidBBLMoved()); fluidProducedAdjusted.update(currentCard.getFluidBBLMovedAdjusted()); oilProduced.update(currentCard.getOilBBLMoved()); waterProduced.update(currentCard.getWaterBBLMoved()); gasProduced.update(currentCard.getGasMCFMoved()); pumpIntakePressure.update(currentCard.getPumpIntakePressure()); surfaceStrokeLength.update(currentCard.getSurfaceStrokeLength()); tubingMovement.update(currentCard.getTubingMovement()); pumpFillPercent.update(currentCard.getFillageCalculated()); db.newCard(currentCard); currentCard = new Card(strokesLifetime); pointCounter = -1; if (strokesSinceStart > startupStrokes){ runStatus = RUNSTATUS_RUNNING; } if (runStatus == RUNSTATUS_RUNNING) { if (pumpFillPercent.getCurrentValue() < pumpOffFillPercentSetpoint) { lowFillageStrokes++; } else { lowFillageStrokes = 0; } if (lowFillageStrokes > pumpOffStrokes) { pumpOff("lowpumpfill"); } } } public void gaugeOff(){ strokeSpeed.endOfDay(); downholeGrossStroke.endOfDay(); downholeNetStroke.endOfDay(); fluidLevel.endOfDay(); fluidLoad.endOfDay(); inflowRate.endOfDay(); peakPolishedRodLoad.endOfDay(); minPolishedRodLoad.endOfDay(); percentRun.endOfDay(); polishedRodHP.endOfDay(); pumpHP.endOfDay(); fluidProduced.endOfDay(); fluidProducedAdjusted.endOfDay(); oilProduced.endOfDay(); waterProduced.endOfDay(); gasProduced.endOfDay(); pumpIntakePressure.endOfDay(); surfaceStrokeLength.endOfDay(); tubingMovement.endOfDay(); strokesToday = 0; } public void eval(){ checkSafeties(); currentSurfacePosition = inclinometer.readScaled(); currentSurfaceLoad = loadCell.readScaled(); LPStatus lastPoint = calc(currentSurfacePosition, currentSurfaceLoad); if (lastPoint.getStatus() == GOOD_STATUS){ currentDownholePosition = lastPoint.getPosition(); currentDownholeLoad = lastPoint.getLoad(); } if(runStatus == RUNSTATUS_STARTING || runStatus == RUNSTATUS_RUNNING) { if (lastPoint.getStatus() == GOOD_STATUS) { currentCard.setSurfacePosition(pointCounter, currentSurfacePosition); currentCard.setSurfaceLoad(pointCounter, currentSurfaceLoad); currentCard.setDownholePosition(pointCounter, currentDownholePosition); currentCard.setDownholeLoad(pointCounter, currentDownholeLoad); } if (inclinometer.getHistory(0) > inclinometer.getHistory(1)) direction = DIRECTION_UP; else if (inclinometer.getHistory(0) < inclinometer.getHistory(1)) direction = DIRECTION_DOWN; if (direction == DIRECTION_UP && lastDirection == DIRECTION_DOWN && pointCounter > 0) { endOfStroke(); } lastDirection = direction; pointCounter++; } if (runStatus == RUNSTATUS_PUMPEDOFF){ minutesSincePumpOff = ChronoUnit.MINUTES.between(ZonedDateTime.now(), pumpedOffTime); if(minutesSincePumpOff_last != minutesSincePumpOff){ System.out.println("Restarting in " + (pumpOffDowntimeMinutes - minutesSincePumpOff) + " minutes."); minutesSincePumpOff_last = minutesSincePumpOff; } if (minutesSincePumpOff > pumpOffDowntimeMinutes){ restart("pumpoffdowntime"); } } if(isNewDay()){ gaugeOff(); } } public void eval(int simPoint){ checkSafeties(); currentSurfacePosition = inclinometer.readScaledSim(sim.getPositionAtIndex(simPoint)); currentSurfaceLoad = loadCell.readScaledSim(sim.getLoadAtIndex(simPoint)); LPStatus lastPoint = calc(currentSurfacePosition, currentSurfaceLoad); if (lastPoint.getStatus() == GOOD_STATUS){ currentDownholePosition = lastPoint.getPosition(); currentDownholeLoad = lastPoint.getLoad(); } if(runStatus == RUNSTATUS_STARTING || runStatus == RUNSTATUS_RUNNING) { if (lastPoint.getStatus() == GOOD_STATUS) { currentCard.setSurfacePosition(pointCounter, currentSurfacePosition); currentCard.setSurfaceLoad(pointCounter, currentSurfaceLoad); currentCard.setDownholePosition(pointCounter, currentDownholePosition); currentCard.setDownholeLoad(pointCounter, currentDownholeLoad); } if (inclinometer.getHistory(0) > inclinometer.getHistory(1)) direction = DIRECTION_UP; else if (inclinometer.getHistory(0) < inclinometer.getHistory(1)) direction = DIRECTION_DOWN; if (direction == DIRECTION_UP && lastDirection == DIRECTION_DOWN && pointCounter > 0) { endOfStroke(); } lastDirection = direction; pointCounter++; } if (runStatus == RUNSTATUS_PUMPEDOFF){ minutesSincePumpOff = ChronoUnit.MINUTES.between(pumpedOffTime, ZonedDateTime.now()); if(minutesSincePumpOff_last != minutesSincePumpOff){ System.out.println("Restarting in " + (pumpOffDowntimeMinutes - minutesSincePumpOff) + " minutes."); minutesSincePumpOff_last = minutesSincePumpOff; } if (minutesSincePumpOff > pumpOffDowntimeMinutes){ restart("pumpoffdowntime"); } } if(isNewDay()){ gaugeOff(); } } public void printTotals(){ V2_AsciiTable taperTable = new V2_AsciiTable(); taperTable.addRule(); taperTable.addRow("Measuremnt", "Average", "Total"); taperTable.addStrongRule(); taperTable.addRow("Stroke Speed", strokeSpeed.getAverage(), strokeSpeed.getTotal()); taperTable.addRow("DH Gross Stroke", downholeGrossStroke.getAverage(), downholeGrossStroke.getTotal()); taperTable.addRow("DH Net Stroke", downholeNetStroke.getAverage(), downholeNetStroke.getTotal()); taperTable.addRow("Fluid Level", fluidLevel.getAverage(), fluidLevel.getTotal()); taperTable.addRow("Fluid Load", fluidLoad.getAverage(), fluidLoad.getTotal()); taperTable.addRow("Peak PR Load", peakPolishedRodLoad.getAverage(), peakPolishedRodLoad.getTotal()); taperTable.addRow("Min PR Load", minPolishedRodLoad.getAverage(), minPolishedRodLoad.getTotal()); taperTable.addRow("PR HP", polishedRodHP.getAverage(), polishedRodHP.getTotal()); taperTable.addRow("Pump HP", pumpHP.getAverage(), pumpHP.getTotal()); taperTable.addRow("Fluid Produced", fluidProduced.getAverage(), fluidProduced.getTotal()); taperTable.addRow("Oil Produced", oilProduced.getAverage(), oilProduced.getTotal()); taperTable.addRow("Water Produced", waterProduced.getAverage(), waterProduced.getTotal()); taperTable.addRow("Gas Produced", gasProduced.getAverage(), gasProduced.getTotal()); taperTable.addRow("Pump Intake Pressure", pumpIntakePressure.getAverage(), pumpIntakePressure.getTotal()); taperTable.addRow("Surf Stroke Length", surfaceStrokeLength.getAverage(), surfaceStrokeLength.getTotal()); taperTable.addRow("Tubing Movement", tubingMovement.getAverage(), tubingMovement.getTotal()); taperTable.addRule(); V2_AsciiTableRenderer rend = new V2_AsciiTableRenderer(); rend.setTheme(V2_E_TableThemes.UTF_LIGHT.get()); rend.setWidth(new WidthAbsoluteEven(100)); RenderedTable rt = rend.render(taperTable); System.out.println(rt); System.out.println(); } public static void main( String[] args ){ Well thisWell = new Well(args[1], 99, 99, 99); thisWell.parseJSONFile(args[0]); thisWell.printTapers(); } }