443 lines
28 KiB
Python
443 lines
28 KiB
Python
# Python program to analyze cards
|
|
import matplotlib.pyplot as plt
|
|
import numpy as np
|
|
import readCardFile as rcf
|
|
|
|
# Setup Data
|
|
slices = 250 # number of horizontal slices to evaluate
|
|
pctTop = 10 # when finding corners, percent below maximum to search
|
|
pctBottom = 10 # when finding corners, percent above minimum to search
|
|
pumpTapPercent = 35 # percentage of downhole load span to be considered 100% tapping
|
|
tubingMovementPctStroke = 25 # percentage of stroke taken up by tubing movement to consider score of 100
|
|
|
|
downhole = rcf.readFile()[1]
|
|
|
|
# Mostly Full Card
|
|
# downhole = [[64.577, -4503.565], [65.055, -4507.592], [65.432, -4493.037], [65.851, -4599.1], [66.291, -4735.827], [66.561, -4853.273], [66.682, -4899.307], [66.787, -4918.142], [66.89, -4929.726], [67.001, -4983.405], [67.108, -4978.893], [67.128, -4937.433], [67.101, -4955.286], [67.019, -4990.838], [66.86, -5014.888], [66.609, -5034.405], [66.286, -5053.554], [65.872, -5107.013], [65.38, -5126.4], [64.761, -5138.246], [64.073, -5152.267], [63.318, -5237.705], [62.387, -5360.465], [61.333, -5386.295], [60.202, -5427.148], [59.121, -5698.709], [58.068, -5775.45], [56.748, -5961.322], [55.738, -6598.427], [54.947, -7069.938], [54.472, -7458.419], [54.264, -7766.768], [54.231, -7900.823], [54.274, -7919.208], [54.493, -8065.488], [54.798, -8058.136], [54.94, -7923.052], [55.063, -7913.114], [55.224, -7932.654], [55.324, -7964.23], [55.394, -7934.037], [55.398, -7917.927], [55.438, -8037.331], [55.527, -8057.723], [55.46, -7971.96], [55.368, -7845.327], [55.488, -8151.271], [55.302, -8644.739], [54.942, -8993.722], [54.33, -9503.194], [53.228, -9900.778], [51.617, -9812.004], [49.266, -9620.005], [46.471, -9609.26], [43.288, -9329.185], [39.96, -9250.057], [36.341, -9366.431], [32.531, -9379.656], [28.688, -9408.648], [24.829, -9418.401], [20.988, -9513.613], [17.209, -9660.981], [13.48, -9622.327], [9.957, -9654.526], [6.484, -9772.297], [3.101, -9758.53], [-0.097, -9737.41], [-3.072, -9726.858], [-5.92, -9730.714], [-8.554, -9710.393], [-11.079, -9383.728], [-13.231, -9137.783], [-15.146, -8857.06], [-16.522, -8288.292], [-17.249, -7753.214], [-17.524, -6990.508], [-17.425, -6589.685], [-16.774, -6192.202], [-16.215, -5397.993], [-15.67, -4906.086], [-15.105, -4564.741], [-14.478, -4324.163], [-13.793, -4192.837], [-13.014, -4094.249], [-12.14, -3961.145], [-11.155, -3976.249], [-9.957, -4033.805], [-8.689, -4038.992], [-7.438, -4063.336], [-6.192, -3982.634], [-4.922, -3940.978], [-3.642, -3944.722], [-2.381, -3955.24], [-1.204, -4037.927], [-0.213, -4031.55], [0.623, -4048.03], [1.238, -3668.058], [2.166, -3223.797], [3.326, -3193.519], [4.675, -3049.454], [6.405, -2941.393], [8.566, -3218.628], [11.119, -3568.049], [13.674, -3672.504], [16.261, -3844.035], [18.726, -3851.298], [21.175, -3785.033], [23.526, -3837.698], [25.733, -3859.446], [27.746, -3803.885], [29.603, -3773.95], [31.459, -3777.372], [33.243, -3804.467], [34.927, -3828.283], [36.52, -3770.409], [38.017, -3681.814], [39.579, -3626.998], [41.175, -3641.395], [42.788, -3743.97], [44.416, -3937.624], [45.946, -4236.965], [47.175, -4377.768], [48.21, -4438.309], [48.838, -4537.2], [49.129, -4478.764], [49.196, -4308.402], [49.137, -4262.084], [49.143, -4247.508], [49.098, -4114.916], [49.084, -4100.473], [49.072, -4051.415], [49.196, -3999.507], [49.395, -4009.326], [49.634, -4000.167], [49.88, -3973.371], [50.084, -3918.636], [50.357, -3886.275], [50.697, -3936.03], [50.981, -4002.471], [51.209, -4039.17], [51.401, -4074.127], [51.553, -4057.877], [51.723, -4087.47], [52.184, -4223.707], [52.507, -4084.271], [53.238, -3994.224], [54.011, -3849.528], [54.93, -3743.578], [55.856, -3797.639], [56.748, -3817.357], [57.653, -3869.861], [58.552, -3891.538], [59.342, -3931.89], [60.089, -4015.883], [60.804, -4092.087]]
|
|
|
|
# Tubing Movement Card
|
|
#downhole = [[46.83,-2887.081], [46.892,-2852.452], [46.914,-2878.963], [46.902,-2899.007], [46.927,-2914.736], [47.007,-3009.142], [47.098,-3044.16], [47.092,-2973.454], [47.047,-2927.351], [47.051,-2983.958], [47.026,-3072.558], [46.947,-3099.861], [46.85,-3086.603], [46.764,-3128.667], [46.706,-3222.279], [46.686,-3412.667], [46.643,-3492.5], [46.517,-3486.271], [46.347,-3520.645], [46.154,-3498.79], [45.943,-3570.53], [45.735,-3729.349], [45.457,-3834.83], [45.112,-3882.304], [44.779,-3951.678], [44.53,-4202.284], [44.259,-4344.169], [43.867,-4339.462], [43.489,-4462.086], [43.164,-4546.753], [42.917,-4713.565], [42.674,-4933.95], [42.349,-5087.896], [42.091,-5281.532], [41.858,-5409.675], [41.692,-5623.63], [41.597,-5789.314], [41.413,-5789.535], [41.212,-5854.792], [41.086,-5882.121], [41.039,-6019.487], [41.043,-6218.706], [40.973,-6308.513], [40.906,-6457.92], [40.837,-6594.607], [40.856,-6745.795], [40.883,-6767.062], [40.83,-6794.824], [40.797,-6933.782], [40.778,-6895.658], [40.822,-7034.814], [40.884,-7296.804], [40.818,-7419.478], [40.803,-7614.82], [40.776,-7751.075], [40.777,-8017.832], [40.764,-8241.186], [40.682,-8306.266], [40.554,-8463.126], [40.378,-8506.211], [40.272,-8608.049], [40.154,-8848.913], [39.906,-8996.566], [39.656,-9101.418], [39.305,-9067.18], [38.986,-9168.235], [38.634,-9203.583], [38.111,-9143.733], [37.598,-9187.537], [37.048,-9105.862], [36.496,-9175.496], [35.831,-9297.358], [35.077,-9295.168], [34.328,-9385.535], [33.5,-9417.49], [32.723,-9524.418], [31.903,-9543.187], [30.982,-9451.246], [30.098,-9542.783], [29.151,-9510.178], [28.189,-9502.326], [27.179,-9617.115], [26.052,-9694.688], [24.867,-9766.146], [23.598,-9761.514], [22.288,-9798.367], [20.981,-9780.708], [19.63,-9698.224], [18.306,-9740.019], [16.958,-9686.539], [15.724,-9736.812], [14.441,-9795.19], [13.139,-9681.094], [11.895,-9693.57], [10.726,-9760.314], [9.592,-9739.54], [8.484,-9680.257], [7.457,-9688.138], [6.545,-9698.494], [5.685,-9580.339], [4.955,-9585.38], [4.243,-9641.511], [3.558,-9554.533], [2.959,-9510.332], [2.426,-9502.439], [1.926,-9394.177], [1.461,-9293.815], [1.131,-9240.729], [0.896,-9156.458], [0.718,-9034.355], [0.661,-8899.793], [0.632,-8803.163], [0.698,-8727.494], [0.864,-8618.893], [1.119,-8519.666], [1.39,-8330.483], [1.644,-8299.007], [2,-8344.218], [2.361,-8159.301], [2.726,-8045.124], [3.063,-7967.46], [3.394,-7865.336], [3.799,-7748.28], [4.204,-7632.871], [4.584,-7505.887], [4.938,-7339.214], [5.354,-7351.317], [5.839,-7334.936], [6.263,-7134.111], [6.694,-7091.65], [7.063,-6987.825], [7.409,-6818.716], [7.799,-6755.144], [8.162,-6656.295], [8.528,-6602.11], [8.814,-6468.438], [9.062,-6377.223], [9.418,-6363.788], [9.772,-6233.713], [10.132,-6193.403], [10.371,-6052.44], [10.667,-5807.847], [11.056,-5740.982], [11.428,-5666.741], [11.749,-5544.153], [11.979,-5295.527], [12.207,-5178.551], [12.512,-5210.087], [12.812,-5150.446], [13.077,-5086.271], [13.292,-4886.025], [13.578,-4669.326], [13.871,-4606.906], [14.153,-4517.883], [14.414,-4369.183], [14.606,-4223.626], [14.854,-4204.487], [15.15,-4149.035], [15.382,-3974.269], [15.589,-3955.612], [15.735,-3888.778], [15.942,-3758.221], [16.219,-3730.551], [16.522,-3728.962], [16.78,-3677.023], [17.03,-3605.006], [17.333,-3618.731], [17.723,-3620.313], [18.111,-3588.642], [18.507,-3645.753], [18.835,-3584.838], [19.238,-3480.506], [19.803,-3500.618], [20.429,-3489.421], [21.043,-3439.612], [21.66,-3398.009], [22.274,-3438.254], [22.967,-3438.026], [23.667,-3442.973], [24.367,-3499.912], [25.087,-3420.673], [25.911,-3368.711], [26.764,-3426.286], [27.61,-3468.203], [28.469,-3448.212], [29.316,-3352.306], [30.161,-3336.477], [31.077,-3393.436], [31.987,-3440.532], [32.871,-3491.23], [33.721,-3432.89], [34.593,-3393.32], [35.456,-3425.981], [36.298,-3487.22], [37.115,-3508.692], [37.809,-3375.236], [38.502,-3342.76], [39.256,-3397.23], [39.94,-3387.344], [40.551,-3421.552], [41.078,-3365.214], [41.606,-3300.955], [42.124,-3340.255], [42.598,-3304.887], [43.053,-3276.992], [43.491,-3247.673], [43.861,-3218.775], [44.204,-3199.074], [44.529,-3187.272], [44.818,-3259.493], [45.008,-3241.877], [45.174,-3169.097], [45.358,-3180.797], [45.53,-3152.304], [45.695,-3142.974], [45.835,-3115.725], [45.945,-3071.782], [46.048,-2995.923], [46.18,-2967.381], [46.296,-3044.686], [46.328,-2969.671], [46.374,-2862.842], [46.437,-2863.76], [46.491,-2876.354], [46.561,-2888.627], [46.63,-2893.73], [46.688,-2844.868]]
|
|
|
|
# Pumped Off Card
|
|
# downhole=[[46.62,-2712.68], [46.602,-2800.402], [46.534,-2764.056], [46.394,-2655.373], [46.263,-2651.785], [46.138,-2755.013], [45.999,-2767.18], [45.847,-2748.438], [45.736,-2776.021], [45.606,-2749.383], [45.465,-2784.358], [45.327,-2888.547], [45.14,-2896.921], [44.948,-2943.494], [44.79,-3047.353], [44.632,-3124.92], [44.491,-3203.635], [44.329,-3212.191], [44.211,-3351.46], [44.126,-3434.87], [43.893,-3402.475], [43.648,-3489.605], [43.373,-3496.714], [43.076,-3609.12], [42.726,-3652.814], [42.381,-3682.839], [42.109,-3868.039], [41.777,-4001.037], [41.381,-4092.478], [40.923,-4032.864], [40.407,-4011.32], [39.87,-4078.828], [39.252,-4021.814], [38.533,-4000.884], [37.709,-4023.979], [36.817,-4012.084], [35.884,-4119.401], [34.922,-4226.103], [33.985,-4364.747], [33.015,-4340.242], [31.772,-4084.583], [30.454,-4186.653], [29.164,-4334.347], [27.943,-4459.974], [26.647,-4724.885], [25.311,-4907.698], [24.035,-5196.977], [22.856,-5536.343], [21.915,-5988.342], [21.099,-6207.023], [20.393,-6277.558], [19.88,-6619.778], [19.636,-6877.075], [19.486,-7004.481], [19.361,-7169.644], [19.309,-7222.818], [19.293,-7321.001], [19.319,-7506.76], [19.38,-7669.473], [19.468,-7688.039], [19.452,-7580.956], [19.539,-7603.85], [19.69,-7499.871], [19.966,-7604.458], [20.234,-7987.768], [20.343,-8046.571], [20.515,-8010.688], [20.819,-8139.062], [21.135,-8352.702], [21.437,-8524.034], [21.635,-8747.591], [21.806,-9045.313], [21.887,-9210.366], [21.91,-9533.773], [21.765,-9970.918], [21.268,-10077.19], [20.656,-10106.954], [19.921,-10233.764], [18.956,-10184.094], [17.902,-10141.401], [16.742,-10103.894], [15.569,-9961.449], [14.364,-9788.658], [13.214,-9741.754], [11.959,-9742.078], [10.664,-9667.238], [9.451,-9661.859], [8.197,-9555.982], [6.951,-9387.272], [5.788,-9378.236], [4.736,-9395.63], [3.779,-9331.787], [2.846,-9178.237], [1.996,-9134.094], [1.157,-9067.718], [0.349,-8902.578], [-0.307,-8899.076], [-0.842,-8825.375], [-1.417,-8495.976], [-1.925,-8406.432], [-2.162,-8415.77], [-2.313,-8165.475], [-2.316,-7971.104], [-2.164,-7774.518], [-1.868,-7469.596], [-1.405,-7125.546], [-0.836,-6804.768], [-0.191,-6500.219], [0.427,-6154.017], [1.025,-5891.592], [1.71,-5692.756], [2.373,-5484.625], [2.975,-5384.557], [3.524,-5202.852], [4.052,-4905.002], [4.605,-4745.705], [5.159,-4676.503], [5.646,-4588.479], [6.087,-4447.826], [6.415,-4363.645], [6.781,-4308.298], [7.208,-4210.163], [7.607,-4341.792], [7.823,-4231.768], [8.1,-4013.293], [8.469,-4037.254], [8.784,-3968.269], [9.05,-3848.801], [9.317,-3704.935], [9.615,-3745.845], [9.999,-3745.517], [10.352,-3575.309], [10.728,-3533.071], [11.123,-3454.092], [11.655,-3307.476], [12.318,-3219.887], [13.053,-3125.011], [13.907,-3064.4], [14.821,-3024.716], [15.774,-3057.919], [16.851,-3077.195], [17.929,-3046.074], [19.11,-3149.958], [20.246,-3187.356], [21.4,-3081.013], [22.649,-3090.806], [23.927,-3123.975], [25.222,-3158.243], [26.47,-3109.859], [27.725,-3131.643], [29.074,-3250.88], [30.339,-3222.404], [31.573,-3276.256], [32.746,-3184.676], [33.961,-3054.106], [35.186,-3105.254], [36.363,-3140.104], [37.512,-3103.287], [38.66,-3028.344], [39.784,-3065.363], [40.878,-3106.046], [41.916,-3093.05], [42.889,-3138.815], [43.748,-3091.373], [44.615,-3086.79], [45.405,-3115.451], [46.112,-3065.278], [46.748,-3048.583], [47.3,-3038.282], [47.785,-3037.967], [48.197,-2976.641], [48.504,-2874.274], [48.737,-2905.508], [48.888,-2892.011], [49.067,-2823.676], [49.241,-2883.689], [49.346,-2879.874], [49.42,-2870.957], [49.467,-2926.496], [49.459,-2906.499]]
|
|
|
|
scores = {"fullPump": 0,
|
|
"tubingMovement": 0,
|
|
"fluidPound": 0,
|
|
"gasInterference": 0,
|
|
"pumpTappingTop": 0,
|
|
"pumpTappingBottom": 0,
|
|
"bentBarrel": 0,
|
|
"wornPlunger": 0,
|
|
"wornStandingBill": 0,
|
|
"wornBarrel": 0,
|
|
"fluidFriction": 0,
|
|
"dragFriction": 0,
|
|
"rodPart": 0}
|
|
|
|
|
|
def printScores():
|
|
print("Full Pump: {:.5f}".format(scores['fullPump']))
|
|
print("Tubing Movement: {:.5f}".format(scores['tubingMovement']))
|
|
print("Fluid Pound: {:.5f}".format(scores['fluidPound']))
|
|
print("Gas Interference: {:.5f}".format(scores['gasInterference']))
|
|
print("Pump Tapping - Top: {:.5f}".format(scores['pumpTappingTop']))
|
|
print("Pump Tapping - Bottom: {:.5f}".format(scores['pumpTappingBottom']))
|
|
print("Bent Barrel: {:.5f}".format(scores['bentBarrel']))
|
|
print("Worn Plunger: {:.5f}".format(scores['wornPlunger']))
|
|
print("Worn Standing Bill: {:.5f}".format(scores['wornStandingBill']))
|
|
print("Worn Barrel: {:.5f}".format(scores['wornBarrel']))
|
|
print("Fluid Friction: {:.5f}".format(scores['fluidFriction']))
|
|
print("Drag Friction: {:.5f}".format(scores['dragFriction']))
|
|
print("Rod Part: {:.5f}".format(scores['rodPart']))
|
|
|
|
|
|
# helper functions
|
|
def __find_incremental_load(target_position, downholeArray, lastResult):
|
|
up_point_greater = [0, 0]
|
|
up_point_lesser = [0, 0]
|
|
down_point_greater = [0, 0]
|
|
down_point_lesser = [0, 0]
|
|
up_point_found = False
|
|
down_point_found = False
|
|
for ind in range(1, len(downholeArray)):
|
|
# print(downholeArray[i][0])
|
|
if (downholeArray[ind][0] > target_position) and (downholeArray[ind - 1][0] <= target_position):
|
|
up_point_greater = [downholeArray[ind][0], downholeArray[ind][1]]
|
|
up_point_lesser = [downholeArray[ind - 1][0], downholeArray[ind - 1][1]]
|
|
up_point_found = True
|
|
if (downholeArray[ind][0] <= target_position) and (downholeArray[ind - 1][0] > target_position):
|
|
down_point_greater = [downholeArray[ind][0], downholeArray[ind][1]]
|
|
down_point_lesser = [downholeArray[ind - 1][0], downholeArray[ind - 1][1]]
|
|
down_point_found = True
|
|
if up_point_found & down_point_found:
|
|
m_up = (up_point_greater[1] - up_point_lesser[1]) / (up_point_greater[0] - up_point_lesser[0])
|
|
b_up = up_point_greater[1] - (m_up * up_point_greater[0])
|
|
up_middle = (m_up * target_position) + b_up
|
|
|
|
m_down = (down_point_greater[1] - down_point_lesser[1]) / (down_point_greater[0] - down_point_lesser[0])
|
|
b_down = down_point_greater[1] - (m_down * down_point_greater[0])
|
|
down_middle = (m_down * target_position) + b_down
|
|
|
|
return [up_middle - down_middle, up_middle, down_middle]
|
|
else:
|
|
return lastResult
|
|
|
|
|
|
def __find_nearest_point_to_load(direction, card_half, target_load, ptArray, minPos, maxPos):
|
|
left_ignore = minPos + (minPos + maxPos) * 0.75
|
|
right_ignore = minPos + (minPos + maxPos) * 0.25
|
|
|
|
if direction == "up":
|
|
for up_i in range(1, len(ptArray)):
|
|
if card_half == "right" and ptArray[up_i][0] >= right_ignore:
|
|
if (ptArray[up_i][1] <= target_load) & (ptArray[up_i - 1][1] > target_load):
|
|
return [ptArray[up_i][0], ptArray[up_i][1]]
|
|
elif card_half == "left" and ptArray[up_i][0] <= left_ignore:
|
|
if (ptArray[up_i][1] >= target_load) & (ptArray[up_i - 1][1] < target_load):
|
|
return [ptArray[up_i][0], ptArray[up_i][1]]
|
|
else:
|
|
for down_i in range(len(ptArray) - 1, 1, -1):
|
|
if card_half == "right" and ptArray[down_i][0] >= right_ignore:
|
|
if (ptArray[down_i][1] >= target_load) & (ptArray[down_i - 1][1] < target_load):
|
|
return [ptArray[down_i][0], ptArray[down_i][1]]
|
|
elif card_half == "left" and ptArray[down_i][0] <= left_ignore:
|
|
if (ptArray[down_i][1] <= target_load) & (ptArray[down_i - 1][1] > target_load):
|
|
return [ptArray[down_i][0], ptArray[down_i][1]]
|
|
|
|
|
|
def countPointsBetween(posLeft, posRight, dArray):
|
|
topPoints = 0
|
|
bottomPoints = 0
|
|
for l in range(1, len(dArray)):
|
|
if (dArray[l][0] > posLeft) and (dArray[l][0] <= posRight) and (dArray[l][0] < dArray[l - 1][0]):
|
|
topPoints += 1
|
|
if (dArray[l][0] <= posRight) and (dArray[l - 1][0] > posLeft) and (dArray[l][0] > dArray[l - 1][0]):
|
|
bottomPoints += 1
|
|
return [topPoints, bottomPoints]
|
|
|
|
|
|
def average(lis):
|
|
listsum = 0
|
|
for avg_i in range(0, len(lis)):
|
|
listsum += lis[avg_i]
|
|
listavg = listsum / len(lis)
|
|
return listavg
|
|
|
|
|
|
def percentDiff(m1, m2):
|
|
mean = (m1 + m2) / 2
|
|
dif = (m1 - m2) / mean
|
|
if dif < 0:
|
|
return -1 * dif
|
|
else:
|
|
return dif
|
|
|
|
|
|
def percentError(actual, expected):
|
|
err = (actual - expected) / expected
|
|
if err < 0:
|
|
return -1 * err
|
|
else:
|
|
return err
|
|
|
|
# find min & max position
|
|
minPosition = downhole[0][0]
|
|
minPositionPt = [0, 0]
|
|
minLoad = downhole[0][1]
|
|
maxPosition = downhole[0][0]
|
|
maxPositionPt = [0, 0]
|
|
maxLoad = downhole[0][1]
|
|
for i in range(1, len(downhole)):
|
|
if downhole[i][0] < minPosition:
|
|
minPosition = downhole[i][0]
|
|
minPositionPt = downhole[i]
|
|
if downhole[i][0] > maxPosition:
|
|
maxPosition = downhole[i][0]
|
|
maxPositionPt = downhole[i]
|
|
if downhole[i][1] < minLoad:
|
|
minLoad = downhole[i][1]
|
|
if downhole[i][1] > maxLoad:
|
|
maxLoad = downhole[i][1]
|
|
downholeLoadSpan = maxLoad - minLoad
|
|
|
|
# split cards into equal slices
|
|
sliceLength = (maxPosition - minPosition) / (slices + 1)
|
|
slicesAll = []
|
|
topSlices = []
|
|
topSlicesChange = []
|
|
bottomSlices = []
|
|
bottomSlicesChange = []
|
|
sliceDelta = []
|
|
sliceDeltaChange = []
|
|
topPointsBetween = []
|
|
bottomPointsBetween = []
|
|
for j in range(0, slices):
|
|
targetPosition = j * sliceLength + minPosition
|
|
nextTargetPosition = (j + 1) * sliceLength + minPosition
|
|
|
|
# measure points in each slice to look for fast/slow parts
|
|
ptb = countPointsBetween(targetPosition, nextTargetPosition, downhole)
|
|
topPointsBetween.append(ptb[0])
|
|
bottomPointsBetween.append(ptb[1])
|
|
|
|
# interpret mean point of each slice
|
|
if j > 0:
|
|
sliver = __find_incremental_load(targetPosition, downhole, slicesAll[j - 1])
|
|
else:
|
|
sliver = __find_incremental_load(targetPosition, downhole, [0, 0, 0])
|
|
slicesAll.append(sliver)
|
|
sliceDelta.append(sliver[0])
|
|
topSlices.append(sliver[1])
|
|
bottomSlices.append(sliver[2])
|
|
|
|
for k in range(1, slices):
|
|
topSlicesChange.append(topSlices[k] - topSlices[k - 1])
|
|
bottomSlicesChange.append(bottomSlices[k] - bottomSlices[k - 1])
|
|
sliceDeltaChange.append(sliceDelta[k] - sliceDelta[k - 1])
|
|
|
|
topSlicesAverage = average(topSlices)
|
|
bottomSlicesAverage = average(bottomSlices)
|
|
sliceDeltaAverage = average(sliceDelta)
|
|
|
|
# find Graph Corners
|
|
halfLoadLoad = minLoad + downholeLoadSpan / 2
|
|
halfLoad = __find_nearest_point_to_load("up", "left", halfLoadLoad, downhole, minPosition, maxPosition)
|
|
if minPosition < halfLoad[0]:
|
|
corner_bottomLeft = minPositionPt
|
|
corner_topLeft = __find_nearest_point_to_load("up", "left", maxLoad - downholeLoadSpan * (pctTop / 100), downhole,
|
|
minPosition, maxPosition)
|
|
else:
|
|
corner_bottomLeft = __find_nearest_point_to_load("up", "left", minLoad + downholeLoadSpan * (pctBottom / 100),
|
|
downhole, minPosition, maxPosition)
|
|
corner_topLeft = minPositionPt
|
|
corner_topRight = maxPositionPt
|
|
corner_fillage = __find_nearest_point_to_load("up", "right", minLoad + downholeLoadSpan * (pctBottom / 100), downhole,minPosition, maxPosition)
|
|
corner_fullFillage = [corner_bottomLeft[0] + corner_topRight[0] - corner_topLeft[0], corner_bottomLeft[1]]
|
|
|
|
topSlicesAboveAverage = []
|
|
for i in range(0, len(topSlices)):
|
|
if topSlices[i] > topSlicesAverage:
|
|
topSlicesAboveAverage.append(topSlices[i])
|
|
if len(topSlicesAboveAverage) == 0:
|
|
topSlicesAboveAverageAverage = topSlicesAverage
|
|
else:
|
|
topSlicesAboveAverageAverage = average(topSlicesAboveAverage)
|
|
bottomSlicesBelowAverage = []
|
|
for i in range(0, len(bottomSlices)):
|
|
if bottomSlices[i] < bottomSlicesAverage:
|
|
bottomSlicesBelowAverage.append(bottomSlices[i])
|
|
if len(bottomSlicesBelowAverage) == 0:
|
|
bottomSlicesBelowAverageAverage = bottomSlicesAverage
|
|
else:
|
|
bottomSlicesBelowAverageAverage = average(bottomSlicesBelowAverage)
|
|
|
|
# find equation of line between fillage point and the top right
|
|
# y=mx+b
|
|
incompletionLineM = (bottomSlices[-1] - corner_fillage[1]) / ((maxPosition-2*sliceLength) - corner_fillage[0])
|
|
incompletionLineB = bottomSlices[-1] - incompletionLineM * (maxPosition-2*sliceLength)
|
|
def poLine(pos):
|
|
return incompletionLineM * pos + incompletionLineB
|
|
# find slices in the pump-off part of the card
|
|
poSlicesTop = []
|
|
poSlicesBottom = []
|
|
poSlicePos = []
|
|
for i in range(0,len(topSlices)):
|
|
poSlicePosTest = i * sliceLength + minPosition
|
|
if poSlicePosTest > corner_fillage[0]:
|
|
poSlicesBottom.append(bottomSlices[i])
|
|
poSlicesTop.append(topSlices[i])
|
|
poSlicePos.append(poSlicePosTest)
|
|
|
|
# get score for each possible condition based on points. points (0-100) should reflect how closesly the pattern fits
|
|
|
|
# check Full pump
|
|
# ------------------------------------
|
|
scores['fullPump'] = round(
|
|
100 * ((corner_fillage[0] - corner_bottomLeft[0]) / (corner_fullFillage[0] - corner_bottomLeft[0])), 5)
|
|
if scores['fullPump'] > 100.0:
|
|
scores['fullPump'] = 100.0
|
|
|
|
# topPctError = percentDiff(topSlicesAverage,topSlicesAboveAverageAverage)
|
|
# print("topPctError:", topPctError)
|
|
# bottomPctError = percentDiff(bottomSlicesAverage,bottomSlicesBelowAverageAverage)
|
|
# print('bottomPctError:', bottomPctError)
|
|
# scores['fullPump'] = round(100*(percentDiff((topPctError - bottomPctError),0.01)),5)
|
|
|
|
# check tubing movement & drag friction
|
|
# ------------------------------------
|
|
|
|
leftCornerDiff = corner_topLeft[0] - corner_bottomLeft[0]
|
|
if leftCornerDiff > 0:
|
|
# indicates tubing movement
|
|
scores['dragFriction'] = 0.0
|
|
if leftCornerDiff < (maxPosition - minPosition) * (tubingMovementPctStroke / 100):
|
|
scores['tubingMovement'] = round(
|
|
100 * (1 - percentError(leftCornerDiff, (maxPosition - minPosition) * (tubingMovementPctStroke / 100))), 5)
|
|
else:
|
|
scores['tubingMovement'] = 100.0
|
|
else:
|
|
# indicates drag friction
|
|
scores["tubingMovement"] = 0.0
|
|
if abs(leftCornerDiff) < (maxPosition - minPosition) * (tubingMovementPctStroke / 100):
|
|
scores['dragFriction'] = round(100 * (
|
|
1 - percentError(abs(leftCornerDiff), (maxPosition - minPosition) * (tubingMovementPctStroke / 100))), 5)
|
|
else:
|
|
scores['dragFriction'] = 100.0
|
|
|
|
# Check Pump Tapping Top
|
|
# ------------------------------------
|
|
if topSlices[len(topSlices) - 1] > topSlicesAboveAverageAverage:
|
|
scores['pumpTappingTop'] = 100 * percentDiff(topSlices[len(topSlices) - 1], minLoad + downholeLoadSpan * (pumpTapPercent/100))
|
|
else:
|
|
scores['pumpTappingTop'] = 0.0
|
|
|
|
# Check Pump Tapping Bottom
|
|
# ------------------------------------
|
|
if bottomSlices[len(bottomSlices) - 1] < bottomSlicesBelowAverageAverage:
|
|
scores['pumpTappingBottom'] = 100 * percentDiff(bottomSlices[len(bottomSlices) - 1], minLoad - downholeLoadSpan * (pumpTapPercent/100))
|
|
else:
|
|
scores['pumpTappingBottom'] = 0.0
|
|
|
|
# check fluid pound & gas interference
|
|
# ------------------------------------
|
|
distFromPOLine = []
|
|
linear = []
|
|
for i in range(0,len(poSlicePos)):
|
|
distFromPOLine.append(poSlicesBottom[i] - poLine(poSlicePos[i]))
|
|
linear.append(poLine(poSlicePos[i]))
|
|
|
|
pRegC = np.polyfit(poSlicePos, poSlicesBottom, 3)
|
|
fitPOSlicesBottom = []
|
|
for i in range(0, len(poSlicePos)):
|
|
fitPOSlicesBottom.append(pRegC[0] * poSlicePos[i]**3 + pRegC[1] * poSlicePos[i]**2 + pRegC[2] * poSlicePos[i] + pRegC[3])
|
|
gradient1 = np.gradient(fitPOSlicesBottom)
|
|
gradient2 = np.gradient(gradient1)
|
|
|
|
c2 = np.polyfit(poSlicePos, poSlicesBottom, 2)
|
|
c4 = np.polyfit(poSlicePos, poSlicesBottom, 4)
|
|
c5 = np.polyfit(poSlicePos, poSlicesBottom, 5)
|
|
c6 = np.polyfit(poSlicePos, poSlicesBottom, 6)
|
|
|
|
c2g = []
|
|
c4g = []
|
|
c5g = []
|
|
c6g = []
|
|
|
|
for i in range(0, len(poSlicePos)):
|
|
ps = poSlicePos[i]
|
|
c2g.append(c2[0] * ps**2 + c2[1] * ps + c2[2])
|
|
c4g.append(c4[0] * ps**4 + c4[1] * ps**3 + c4[2] * ps**2 + c4[3] * ps + c4[4])
|
|
c5g.append(c5[0] * ps**5 + c5[1] * ps**4 + c5[2] * ps**3 + c5[3] * ps**2 + c5[4] * ps + c5[5])
|
|
c6g.append(c6[0] * ps**6 + c6[1] * ps**5 + c6[2] * ps**4 + c6[3] * ps**3 + c6[4] * ps**2 + c6[5] * ps + c6[6])
|
|
|
|
|
|
inflectionPoint = [0,0]
|
|
for i in range(len(gradient2)-1, 0, -1):
|
|
if (gradient2[i-1] < 0) & (gradient2[i] > 0):
|
|
inflectionPoint = [poSlicePos[i], poSlicesBottom[i], i]
|
|
if inflectionPoint == [0,0]:
|
|
inflectionPoint = [poSlicePos[-1], poSlicesBottom[-1], len(poSlicePos)-1]
|
|
|
|
inflectionLineM = (inflectionPoint[1] - corner_fillage[1]) / ((inflectionPoint[0]) - corner_fillage[0])
|
|
inflectionLineB = inflectionPoint[1] - inflectionLineM * (inflectionPoint[0])
|
|
def inflectionL(pos):
|
|
return inflectionLineM * pos + inflectionLineB
|
|
|
|
inflectionLine = []
|
|
for i in range(0,inflectionPoint[2]+1):
|
|
inflectionLine.append(inflectionL(poSlicePos[i]))
|
|
inflectionError = 0
|
|
for i in range(0,len(inflectionLine)):
|
|
inflectionError += (poSlicesBottom[i] - inflectionLine[i]) * sliceLength
|
|
inflectionArea = (1/2) * (inflectionPoint[1] - corner_fillage[1]) * (inflectionPoint[0] - corner_fillage[0])
|
|
inflectionGasInterferenceError = 1 - (inflectionError / inflectionArea)
|
|
|
|
linearError = 0
|
|
for i in range(0, len(distFromPOLine)):
|
|
linearError += distFromPOLine[i] * sliceLength
|
|
pumpOffArea = (1/2) * (corner_topRight[1] - corner_fillage[1]) * (corner_topRight[0] - corner_fillage[0])
|
|
gasInterferenceError = 1 - (linearError / pumpOffArea)
|
|
|
|
wellFriction = topSlicesAboveAverageAverage - inflectionPoint[1]
|
|
fluidLoad = downholeLoadSpan - wellFriction
|
|
print("Well Friction:", wellFriction)
|
|
print("Fluid Load:", fluidLoad)
|
|
|
|
scores['gasInterference'] = inflectionGasInterferenceError * 100
|
|
if scores['gasInterference'] > 100:
|
|
scores['gasInterference'] = 100.0
|
|
scores['fluidPound'] = 100 - scores['gasInterference']
|
|
|
|
# check worn plunger
|
|
# ------------------------------------
|
|
|
|
# check worn standing bill
|
|
# ------------------------------------
|
|
|
|
# check worn barrel
|
|
# ------------------------------------
|
|
|
|
# check fluid friction
|
|
# ------------------------------------
|
|
|
|
# check rod part
|
|
# ------------------------------------
|
|
if downholeLoadSpan < 1000:
|
|
scores['rodPart'] = 100
|
|
elif downholeLoadSpan < 1500:
|
|
scores['rodPart'] = 75
|
|
elif downholeLoadSpan < 1500:
|
|
scores['rodPart'] = 50
|
|
elif downholeLoadSpan < 2000:
|
|
scores['rodPart'] = 25
|
|
else:
|
|
scores['rodPart'] = 0
|
|
|
|
|
|
|
|
|
|
# compare scores
|
|
printScores()
|
|
# Plot Card on graph
|
|
tpltPos = []
|
|
tpltLod = []
|
|
bpltPos = []
|
|
bpltLod = []
|
|
for i in range(0, len(topSlices)):
|
|
tpltLod.append(topSlices[i])
|
|
tpltPos.append(minPosition + sliceLength * i)
|
|
for i in range(0, len(bottomSlices)):
|
|
bpltLod.append(bottomSlices[i])
|
|
bpltPos.append(minPosition + sliceLength * i)
|
|
|
|
fig = plt.figure(figsize=(12, 9))
|
|
fig.canvas.set_window_title('Downhole Card Analysis')
|
|
|
|
plt.plot(tpltPos, tpltLod, 'g') # plot top points
|
|
plt.plot(bpltPos, bpltLod, 'g') # plot bottom points
|
|
|
|
# using linear error
|
|
# plt.plot([corner_fillage[0], corner_fillage[0],poSlicePos[-1],corner_fillage[0]],[corner_fillage[1],bottomSlices[-1], bottomSlices[-1],corner_fillage[1]],'y')
|
|
# plt.plot(poSlicePos, linear)
|
|
|
|
# using inflection point error
|
|
plt.plot([corner_fillage[0], corner_fillage[0],inflectionPoint[0],corner_fillage[0]],[corner_fillage[1],inflectionPoint[1], inflectionPoint[1],corner_fillage[1]],'purple')
|
|
plt.plot(poSlicePos[0:len(inflectionLine)], inflectionLine, 'red')
|
|
|
|
# average load lines
|
|
# plt.axhline(y=topSlicesAverage, xmin=0, xmax=1)
|
|
# plt.axhline(y=bottomSlicesAverage, xmin=0, xmax=1)
|
|
# plt.axhline(y=topSlicesAboveAverageAverage, xmin=0, xmax=1)
|
|
# plt.axhline(y=bottomSlicesBelowAverageAverage, xmin=0, xmax=1)
|
|
|
|
# curve fit of pump-off
|
|
# plt.plot(poSlicePos, c2g, 'orange')
|
|
plt.plot(poSlicePos, fitPOSlicesBottom, 'b')
|
|
# plt.plot(poSlicePos, c4g, 'red')
|
|
# .plot(poSlicePos, c5g, 'brown')
|
|
# plt.plot(poSlicePos, c6g, 'purple')
|
|
|
|
plt.grid(True)
|
|
plt.show()
|