Files
POC-Java/src/main/java/com/henrypump/poc/Well.java
Patrick McDonagh 6b60d0f806 Adds pump-off logic
2017-02-22 14:57:56 -06:00

1192 lines
44 KiB
Java

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();
}
}