├── README.md ├── Analytical_Solution_Comparison.py ├── Timer.py ├── MHE_PI.py ├── Fluid_Height_Estimation.py ├── MHE_PI_reduced.py ├── MHE_MPC.py ├── Simulation.py └── MHE_MPC_reduced.py /README.md: -------------------------------------------------------------------------------- 1 | The files in this repository are Python source code for the result found in: 2 | 3 | Hansen, B., Tolbert, B., Vernon, C., Hedengren, J.D., Model Predictive Automatic Control of Sucker Rod Pump System with Simulation Case Study, Computers & Chemical Engineering, 2018. https://doi.org/10.1016/j.compchemeng.2018.08.018 4 | 5 | * Analytical_Solution.py - Section 3.3 6 | * Simulation.py - Section 4 7 | * Fluid_Height_Estimation.py - Section 5.1, Figure 18 8 | * Timer.py - Section 6.1 9 | * MHE_PI.py - Section 6.2 10 | * MHE_PI_reduced.py - Section 6.2 with reduced model 11 | * MHE_MPC.py - Section 6.3 12 | * MHE_MPC_reduced.py - Section 6.3 with reduced model 13 | 14 | Preprint: https://apm.byu.edu/prism/uploads/Members/2018_hansen_rod_pumping.pdf 15 | -------------------------------------------------------------------------------- /Analytical_Solution_Comparison.py: -------------------------------------------------------------------------------- 1 | 2 | # compare analytical solution to gekko solution 3 | 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | from gekko import* 7 | from mpl_toolkits.mplot3d.axes3d import Axes3D 8 | 9 | #analytical solution 10 | 11 | def phi(x): 12 | phi = np.cos(x) 13 | return phi 14 | 15 | def psi(x): 16 | psi = np.sin(2*x) 17 | return psi 18 | 19 | def ua(x,t): 20 | #u = np.cos(x)*np.cos(3*t) + 1/6*np.sin(2*x)*np.sin(6*t) 21 | a = 18996.06 # ft/s speed of sound in steel 22 | c = a # 3 (from example problem) 23 | #u = 1/2*(np.cos(x-c*t)+np.cos(x+c*t)) - 1/(4*c)*(np.cos(2*(x+c*t)) -np.cos(2*(x-c*t))) 24 | u = np.cos(x)*np.cos(a*t) + 1/(2*a)*np.sin(2*x)*np.sin(2*a*t) 25 | return u 26 | 27 | 28 | # define time 29 | tf = .0005 30 | npt = 100#101 31 | xf = 2*np.pi 32 | npx = 100#40 33 | 34 | time = np.linspace(0,tf,npt) 35 | xpos = np.linspace(0,xf,npx) 36 | 37 | for i in range(npx): 38 | usol = ua(xpos[i],time) 39 | if i == 0: 40 | ustora = usol 41 | else: 42 | ustora = np.vstack([ustora,usol]) 43 | 44 | for i in range(npt): 45 | if i ==0: 46 | xstor = xpos 47 | else: 48 | xstor = np.vstack([xstor,xpos]) 49 | 50 | for i in range(npx): 51 | if i == 0: 52 | tstor = time 53 | else: 54 | tstor = np.vstack([tstor,time]) 55 | 56 | xstor = xstor.T 57 | 58 | 59 | 60 | #%% 61 | # create gekko model 62 | 63 | m = GEKKO() # (remote=False) for local solution 64 | 65 | m.time = time 66 | 67 | x0 = phi(xpos) 68 | v0 = psi(xpos) 69 | dx = xpos[1]-xpos[0] 70 | a = 18996.06 # ft/s speed of sound in steel 71 | c = m.Const(value = a) 72 | dx = m.Const(value = dx) 73 | u = [m.Var(value = x0[i]) for i in range(npx)] 74 | v = [m.Var(value = v0[i]) for i in range(npx)] 75 | #j = [m.Var() for i in range(npx)] 76 | 77 | [m.Equation(u[i].dt()==v[i]) for i in range(npx)] 78 | # top difference eqution (forward) first order 79 | #m.Equation(v[0].dt()==c**2*(1/dx**2)*(u[2]-2*u[1]+u[0])) 80 | # second order 81 | #m.Equation(v[0].dt()==c**2*(1/dx**2)*(-u[3] + 4*u[2])-5*u[1] +2*u[0]) 82 | # 83 | m.Equation(v[0].dt()==c**2 * (u[1] - 2.0*u[0] + u[npx-1])/dx**2 ) 84 | # central difference (middle) 85 | [m.Equation(v[i+1].dt()== c**2 * (u[i+2] - 2.0*u[i+1] + u[i])/dx**2) for i in range(npx-2) ] 86 | # bottom (backward) first order 87 | #m.Equation(v[npx-1].dt()==c**2*(1/dx**2)*(u[npx-1] -2*u[npx-2]+u[npx-3])) 88 | # second order 89 | m.Equation(v[npx-1].dt()== c**2 * (u[npx-2] - 2.0*u[npx-1] + u[0])/dx**2 ) 90 | # set options 91 | #m.Equaiton(j == v[npx].dt()) 92 | m.options.imode = 4 93 | m.options.solver = 1 94 | m.options.nodes = 3 95 | 96 | m.solve() 97 | 98 | for i in range(npx): 99 | if i ==0: 100 | ustor = np.array([u[i]]) 101 | tstor = np.array([m.time]) 102 | else: 103 | ustor = np.vstack([ustor,u[i]]) 104 | tstor = np.vstack([tstor,m.time]) 105 | 106 | for i in range(npt): 107 | if i == 0: 108 | xstor = xpos 109 | else: 110 | xstor = np.vstack([xstor,xpos]) 111 | xstor = xstor.T 112 | t = tstor 113 | ustor = np.array(ustor) 114 | 115 | #%% 116 | # compute error 117 | error = ustora - ustor 118 | 119 | #%% 120 | fig = plt.figure(1) 121 | ax = fig.add_subplot(1,1,1,projection='3d') 122 | ax.set_xlabel('Distance (ft)', fontsize = 12) 123 | ax.set_ylabel('Time (seconds)', fontsize = 12) 124 | ax.set_zlabel('Position (ft)', fontsize = 12) 125 | #plt.title('Analytical Solution') 126 | ax.set_zlim((-1,1)) 127 | p = ax.plot_wireframe(xstor,tstor,ustora,rstride=1,cstride=1) 128 | fig.savefig('analytical_3d.eps', dpi = 1200, Transparent = True) 129 | fig.show() 130 | 131 | #%% 132 | ### WE USE THIS PLOT FOR THE GEKKO SOLUTION 133 | # 3d gekko solution plot 134 | fig = plt.figure(2) 135 | ax = fig.add_subplot(1,1,1,projection='3d') 136 | ax.set_xlabel('Distance (ft)', fontsize = 12) 137 | ax.set_ylabel('Time (seconds)', fontsize = 12) 138 | ax.set_zlabel('Position (ft)', fontsize = 12) 139 | ax.set_zlim((-1,1)) 140 | #plt.title('GEKKO Solution') 141 | p = ax.plot_wireframe(xstor,tstor,ustor,rstride=1,cstride=1) 142 | fig.savefig('gekko_3d.eps', dpi = 1200, Transparent = True) 143 | fig.show() 144 | 145 | #%% 146 | ### WE USE THIS PLOT FOR THE ANALYTICAL SOLUTION ### 147 | # PLot analytical solution contour 148 | plt.figure() # start a new figure 149 | plt.contour(xstor, tstor, ustora, 150) # using 50 contour lines. 150 | plt.colorbar() # add a colorbar 151 | plt.xlabel('X') # labels for axes 152 | plt.ylabel('Time') 153 | plt.title('Analytical Solution') 154 | plt.show() # show plot 155 | 156 | #%% 157 | # plot gekko solution contour plot 158 | plt.figure() # start a new figure 159 | plt.contour(xstor, tstor, ustor, 150) # using 50 contour lines. 160 | plt.colorbar() # add a colorbar 161 | plt.xlabel('X') # labels for axes 162 | plt.ylabel('Time') 163 | plt.title('GEKKO Solution') 164 | plt.show() # show plot 165 | 166 | #%% 167 | 168 | 169 | # --- setup grid --- 170 | #nx = 200 # number of points in x-direction 171 | #ny = 150 # number of points in y-direction 172 | #x = np.linspace(-5, 5, nx) # nx points equally spaced between -5...5 173 | #y = np.linspace(-6, 6, ny) # ny points equally spaced between -6...6 174 | #X, Y = np.meshgrid(x, y, indexing='ij') # 2D array (matrix) of points across x and y 175 | #Z = np.zeros((nx, ny)) # initialize output of size (nx, ny) 176 | 177 | ### WE USE THIS PLOT FOR THE DIFFERENCE BETWEEN THE ANALYTICAL AND GEKKO SOLUTIONS ### 178 | # --- contour plot --- 179 | plt.figure() # start a new figure 180 | plt.contour(xstor, tstor, error, 150) # using 50 contour lines. 181 | cbar = plt.colorbar() # add a colorbar 182 | cbar.ax.tick_params(labelsize=12) 183 | cbar.set_label('Difference', fontsize=12) 184 | plt.xlabel('Distance (ft)', fontsize = 12) # labels for axes 185 | plt.ylabel('Time (seconds)', fontsize = 12) 186 | #plt.zlabel('Error') 187 | #plt.title('Error (ft)') 188 | plt.savefig('difference_gekko.eps', dpi = 1200, Transparent = True) 189 | plt.show() # show plot 190 | 191 | #%% 192 | per_error = error / ustora 193 | per_error = np.abs(per_error) 194 | 195 | # --- contour plot --- 196 | plt.figure() # start a new figure 197 | plt.contour(xstor, tstor, per_error, 150) # using 50 contour lines. 198 | plt.colorbar(label = 'Error (%)') # add a colorbar 199 | plt.xlabel('X (ft)') # labels for axes 200 | plt.ylabel('Time (seconds)') 201 | #plt.zlabel('Error (%)') 202 | #plt.title('Error (ft)') 203 | plt.savefig('error_gekko.eps') 204 | plt.show() # show plot 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /Timer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Apr 6 13:20:42 2018 4 | 5 | @author: Brandon 6 | """ 7 | 8 | #from __future__ import division # compatibility with python 2.7 9 | from gekko import GEKKO 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | import time 13 | from mpl_toolkits.mplot3d.axes3d import Axes3D 14 | #%% 15 | #Define Timespace for simulation 16 | tf = 1 # sec, length of simulation 17 | npt = 21 # number of time discretizations 18 | nit = 1 # number of itterations to solve 19 | ti = np.linspace(0,tf,npt) # times for plotting 20 | 21 | res = {} #set up on empty dictionary 22 | 23 | # Define Rod Discretizations 24 | TVD_d = 4800 # ft, lenth of rod 25 | npx = 10 # number of rod discretizations 26 | dx = TVD_d/(npx-1) # #ft lenth of rod discretizations 27 | xi = np.linspace(0,TVD_d,npx) # possitions allong rod (for plotting) 28 | 29 | #Set Points 30 | SPM_in = np.ones(npt)*10 31 | 32 | #BuildModle#################################################################### 33 | 34 | sim = GEKKO() 35 | 36 | #Horizon Window 37 | sim.time = np.linspace(0,tf,npt) 38 | 39 | ################################ 40 | # Conventional Rod Pump Unit Geometry 41 | # API geometry dimension values 42 | Ag=210.0 43 | Cg=120.3 44 | Ig=120.0 45 | Pg=148.5 46 | Hg=237.88 47 | Gg=86.88 48 | Rg=47.0 49 | 50 | #lengths from FIG. 1 - Beam Pumping Unit Shown as a Four-Bar Linkage 51 | L_1 = Rg 52 | L_2 = np.sqrt((Hg-Gg)**2.0+Ig**2.0) 53 | L_3 = Cg 54 | L_4 = Pg 55 | L_5 = Ag 56 | 57 | starting_height = 3/4 58 | #Simulation######################################## 59 | 60 | for m in [sim]: 61 | #Constants 62 | m.API = m.Const(value = 45) #API gravity of fluid, unitless 63 | m.c = m.Const(value = 0.000013) #Compressibility, psi^-1 64 | m.k = m.Const(value = 15) #Permeability, md 65 | m.Bo = m.Const(value = 1.2) #FVF, rb/STB 66 | m.A_d = m.FV(value = 2, ub = 8, lb = 1) #Drainage Area, Acres 67 | m.sw = m.Const(value = 0.2) #Water Saturation 68 | m.porosity = m.FV(value = 0.08, ub = .12, lb = 0.07) #Porosity, unitless 69 | m.gamma_E = m.Const(value = 1.78) #Euler Constant 70 | m.C_a = m.Const(value = 31.6) #Drainage Area Shape Factor (Circular) 71 | m.rw = m.Const(value = 0.328) #Welbore radius, ft 72 | m.S = m.FV(value = 0, ub = 10, lb = -5) #unitless 73 | m.u_visc = m.Const(value = 1.5) # Viscosity, cp 74 | m.h_pz = m.Const(value = 8) #pay zone thickness, ft 75 | m.D_t = m.Const(value = 2.5) # tubing diameter, in 76 | m.St_length = m.Const(value = 85) # rod pump stroke length, in 77 | 78 | m.g = m.Const(value = 32.2) # acceleration due to gravity, ft/s^3 79 | m.g_conv= m.Const(value = 32.2) # lbf conversion , lb-ft/s^2-lbf 80 | m.rho_r = m.Const(value = 490) # lbs/ft^3, density of rod steel 81 | m.rho_w = m.Const(value = 62.3 ) # lbs/ft^3, density of water at standard conditions 82 | m.a = m.Const(value =18996.06 ) # ft/s speed of sound in steel 83 | m.D_r = m.Const(value = 1.0) # in, diameter of rod string 84 | m.Ac = m.Const(value= m.D_r.value**2/4.0*np.pi) # in^2, cross sectional area of rod 85 | m.nu = m.Const(value = 0.01) # unitless, damping coefficient 86 | m.pi = m.Const(value=np.pi) 87 | m.E = m.Const(value = 32025000.0) # psi sucker rod modulus of elasticity 88 | m.alpha = m.Const(value = 0.0) # pump parameter, unitless 89 | m.beta = m.Const(value = 1.0) # pump parameter, unitless 90 | 91 | m.L_1 = m.Const(value =L_1) # unit geometry 92 | m.L_2 = m.Const(value =L_2) # unit geometry 93 | m.L_3 = m.Const(value =L_3) # unit geometry 94 | m.L_4 = m.Const(value =L_4) # unit geometry 95 | m.L_5 = m.Const(value =L_5) # unit geometry 96 | m.dx = m.Const(value = dx) # ft delta x 97 | 98 | #Prime Mover Constants (Torque Balance) 99 | m.tau_p = m.Const(value = 3) #tau 100 | m.k_gain = m.Const(value = 1) #one to one ratio between torque and SPM 101 | 102 | ##Economic 103 | m.Weight_lb_ft = m.Const(value = m.rho_r.value*m.Ac.value*m.g.value/m.g_conv/144) #Weight of rod string, lbf/ft 104 | m.Capex = m.Const(value = 200000) #Cost of Pumping Rod Unit,$? 105 | m.P_o = m.Const(value = 50) #Price of Oil, $/STB 106 | m.r = m.Const(value= .12/365) #Daily Discount Rate, % 107 | m.P_th = m.Const(value = 100) #tubing head pressure, psi 108 | m.TVD = m.Const(value = 4800) #true vertical depth, ft 109 | m.E_cost = m.Const(value = 0.13/3600) #Cost of Electricity, cents/Kws 110 | 111 | #Calculated Constants #DO NOT MODIFY# 112 | m.Wr = m.Const(value = m.TVD.value*m.Weight_lb_ft.value) #Weight of entire rod string, lbm 113 | m.D_a = m.Const(value = 2*12*m.rw.value) #Annulus Diameter, in 114 | m.gamma = m.Const(141.5/(m.API.value+131.5)) #Specific gravity of Fluid 115 | m.P_startpump = m.Const(value = 0.433*m.gamma.value*m.TVD.value) #Average Reservoir Pressure at Pump start up 116 | m.Pi = m.Const(value = .433*m.TVD.value) #Initial Reservoir Pressure, psi 117 | 118 | 119 | m.A_t = m.Const((np.pi/4)*m.D_t.value**2) #Cross sectional Area of tubing, in^2 120 | m.A_a = m.Const((np.pi/4)*m.D_a.value**2) #Cross Sectional Area of Annulus, in^2 121 | m.Wf = m.Const(value = m.TVD.value*m.rho_w.value*m.gamma.value*m.g.value/m.g_conv.value*(m.A_t.value-m.Ac.value)/144) # lbf, weight of fluid in tubing 122 | 123 | #MV's 124 | m.SPM_in = m.MV(value = 15, lb = 0, ub = 15) #Rod Pump Pumping Speed/Torque, spm 125 | 126 | #Variables 127 | m.V_i= m.Var(value = 7758*m.A_d.value*m.h_pz.value*m.porosity.value*(1-m.sw.value)/m.Bo.value) #OOIP, stb 128 | m.Vp = m.Var(value = m.V_i.value*(np.exp(m.c.value*(m.Pi.value-m.P_startpump.value))-1)) #initial volume produced prior stb 129 | 130 | 131 | m.h = m.CV(value = 1.0*m.TVD.value*starting_height) # Height, ft 132 | 133 | 134 | m.NPV = m.Var(value = -1.0*m.Capex.value) #Net Present Value, $ 135 | m.y = m.Var( lb = -1, ub = 1) # SIGN(x) 136 | m.sa = m.Var(value = 0, lb = 0) # slack variable a 137 | m.sb = m.Var(value = 0, lb = 0) # slack variable b 138 | m.tsi = m.Var(value = 0.0) # mulation time 139 | m.SPM = m.Var(value = 15) #SPM, strokes/min 140 | #omega = m.Var(value = 0) 141 | m.theta = m.Var(value = 0) # rad i.e sec^-1 crank angle of surface unit 142 | m.u = [m.SV(value = 9.22) for i in range(npx)] # relative position of each rod segment 143 | m.v = [m.Var(value = 0.0) for i in range(npx)] # velocity of reach rod segment 144 | m.f = [m.SV(value = 0.0) for i in range (npx)] # load at each rod segment 145 | m.P = m.Var(value = 1e-6) # unitless, load at the pump 146 | 147 | 148 | ## State Variables 149 | m.P_res = m.Var(value = m.P_startpump.value*1.0) #Current Reservoir Pressure , psi 150 | m.P_wf = m.Var(value = 0.433*m.gamma*m.h.value) #Bottomhole Flowing Pressure, psi 151 | m.q_in = m.Var(value = (1/86400)*m.k.value*m.h_pz.value*(m.P_res.value-m.P_wf.value)/(141.2*m.Bo.value*m.u_visc.value*((1/2)*np.log(4*m.A_d.value/(m.gamma_E.value*m.C_a.value*m.rw.value**2)) + m.S.value))) #IPR-VLP Flow rate, STB/s 152 | m.q_out = m.Var(value = 0) # Outgoing Flow Rate, STB/s 153 | m.t = m.Var(value = 0) #Time, days 154 | m.W_rod = m.Var(value = (1.0962)*m.q_out.value*(m.P_th.value-m.P_wf.value + .433*m.gamma.value*m.TVD.value) + (4.7053e-7)*m.Wr.value*m.St_length.value*m.SPM.value) #Work supplied by electric Motor, KW 155 | 156 | #Intermediates 157 | m.hs = m.Intermediate(m.sqrt(L_1**2 +L_2**2 + 2 *L_1 *L_2 *m.cos(m.theta))) 158 | m.V_rp = m.Intermediate((1/9702)*(np.pi/4)*m.D_t**2*m.St_length) #Volume Extracted per stroke length, STB 159 | 160 | #Equations 161 | ##AlgebraicEqns 162 | m.Equation(m.V_i == 7758*m.A_d*m.h_pz*m.porosity*(1-m.sw)/m.Bo) 163 | m.Equation(m.P_wf == 0.433*m.gamma*m.h) 164 | m.Equation(m.P_res == m.Pi-(1/m.c)*m.log((m.Vp/m.V_i)+1)) 165 | m.Equation(m.q_in == (1/86400)*m.k*m.h_pz*(m.P_res-m.P_wf)/(141.2*m.Bo*m.u_visc*((1/2)*m.log(4*m.A_d/(m.gamma_E*m.C_a*m.rw**2)) + m.S))) #STB/s 166 | m.Equation(m.W_rod == (1.0962)*m.q_out*(m.P_th-m.P_wf + .433*m.gamma*m.TVD) + (4.7053e-7)*m.Wr*m.St_length*m.SPM) 167 | 168 | #Prime Mover Equations- Torque Balance and Kinematic Eqns 169 | m.Equation(m.SPM.dt() == -(1/m.tau_p)*m.SPM + (m.k_gain/m.tau_p)*m.SPM_in) 170 | m.Equation((2*m.pi/60)*m.SPM == m.theta.dt()) 171 | 172 | #m.Equation(theta ==2.5118541087922712 +tsi*SPM_in * 2.0 * 3.147 / 60.0) # convert time to angle in radians 173 | #m.Equation(SPM == omega/(2*pi)/60) 174 | 175 | m.Equation(m.u[0] == (1/12)*L_5*(m.asin(L_1*m.sin(m.theta)/m.hs)+m.acos((m.hs**2+L_3**2-L_4**2)/(2*L_3*m.hs)))) # position of polished rod, inches 176 | [m.Equation(m.v[i+1].dt()== m.a**2 * (m.u[i+2] - 2.0*m.u[i+1] + m.u[i])/m.dx**2 - m.pi*m.a*m.nu/(2.0*m.TVD)*m.v[i+1] - (1-m.rho_w*m.gamma/m.rho_r)*m.g) for i in range(npx-2) ]# wave equation 177 | m.Equation(m.q_out == m.A_t * m.u[-1].dt()*12/231/42 * (1+m.y)/2) # rate of fluid production, barrels/ 178 | #m.Equation(q_out == (1/60)*V_rp*SPM) 179 | 180 | # Equations for calculating rod loading 181 | # Load at surface 182 | m.Equation(m.f[0] == m.E*m.Ac*1/2/m.dx *(-m.u[2] + 4*m.u[1] -3*m.u[0])) 183 | # Load at pump 184 | #m.Equation(f[npx-1] == E*Ac* 1/2.0/dx *(3*u[npx-1] - 4*u[npx-2] + u[npx-3])) 185 | m.Equation(m.f[npx-1] == m.E*m.Ac* m.P) 186 | # load at intermediate points 187 | [m.Equation(m.f[1+i] == m.E*m.Ac*1/2.0/dx*(m.u[i+2] - m.u[i])) for i in range(npx-2)] 188 | # pump boundary 189 | m.Equation( m.u[npx-1]*m.alpha + (m.u[npx-1] - m.u[npx-2])/dx == m.P) 190 | #add in signum for lifting and lowering conditions 191 | m.Equation(m.v[-1] == m.sb - m.sa ) 192 | m.Equation(m.P == -((m.Wf- (m.A_t - m.Ac)*m.P_wf)/m.E/m.Ac) * (1 + m.y)/2 ) # -P_wf*A_t 193 | 194 | 195 | ##DifferentialEans 196 | m.Equation(m.t.dt() == 1) 197 | m.Equation(m.Vp.dt() == m.q_in) 198 | m.Equation(m.NPV.dt() == (m.P_o*m.q_out-m.E_cost*m.W_rod)*m.exp(-m.r*m.t)) 199 | m.Equation(m.h.dt() == (1617/2)*(m.q_in - m.q_out)/(m.A_a -m.A_t)) 200 | m.Equation(m.tsi.dt()==1.0) # create time variable 201 | [m.Equation(m.u[i].dt()==m.v[i]) for i in range(npx)] # velocity of rod string 202 | 203 | # Set Objectives ################################################## 204 | m.Obj((m.sa*(1+m.y) + m.sb*(1-m.y))) # objective function to make signum work. 205 | #m.Equation((m.sa*(1+m.y) + m.sb*(1-m.y))<= 1e-6) 206 | 207 | 208 | #SetGlobalOptions(Simulation)############################################################## 209 | sim.options.IMODE = 5 # 4 = Dynamic Simulation (Seqential) 210 | sim.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 211 | sim.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 212 | sim.options.time_shift = npt-1 # time shift forward for multiple simulations 213 | sim.options.MAX_ITER = 450 214 | 215 | #SetLocalOptions############################################################### 216 | #N/A 217 | sim.SPM_in.FSTATUS = 1 # accept measurments 218 | sim.SPM_in.STATUS = 0 # don't let optimizer change (simulation) 219 | 220 | 221 | 222 | #Solve######################################################################### 223 | #%% 224 | # Solve the simulation in a loop to simulate a longer horizon 225 | loops = 180 # number of steps forward in time (previously 145 sec) 226 | 227 | 228 | 229 | 230 | ############################################################### 231 | trigger = 30 # ammount of time to shut unit down when low level is detected 232 | for i in range(loops): 233 | 234 | # simulate system for 1 second 235 | sim.solve() 236 | if i == 0: 237 | # Create and store results 238 | sim_ts = np.array(sim.tsi.value) # simulation time storage 239 | sim_us = [np.array(sim.u[i].value) for i in range(npx)] # u relative position storage 240 | sim_vs = [np.array(sim.v[i].value) for i in range(npx)] 241 | sim_fs = [np.array(sim.f[i].value) for i in range(npx)] # dynamic load storage 242 | sim_hstor = np.array(sim.h.value) # height of fluid in annulus storage 243 | sim_q_ins= np.array(sim.q_in.value) # reservoir influx storage 244 | sim_q_outs = np.array(sim.q_out.value) # production rate storage 245 | sim_P_ress = np.array(sim.P_res.value) # reservoir pressure storage 246 | sim_Vps = np.array(sim.Vp.value) # cumulative volume produced storage 247 | sim_NPVs = np.array(sim.NPV.value) # NPV storage 248 | sim_W_rods = np.array(sim.W_rod.value) # work of rod (work to lift fluid) storage 249 | sim_SPMs = np.array(sim.SPM_in.value) # Strokes per minute/ Torque storage Set Points 250 | sim_SPMr = np.array(sim.SPM.value) #SPM storage 251 | sim_thetas = np.array(sim.theta.value)#Theta storage 252 | sim_P_wfs = np.array(sim.P_wf.value) # bottom hole pressure storage 253 | sim_ys = np.array(sim.y.value) # sign of du/dt storage 254 | 255 | elif i>0: 256 | sim_ts = np.append(sim_ts,sim.tsi.value) # simulation time storage 257 | sim_us = [np.append(sim_us[i],sim.u[i].value) for i in range(npx)] # u relative position storage 258 | sim_vs = [np.append(sim_vs[i],sim.v[i].value) for i in range(npx)] 259 | sim_fs = [np.append(sim_fs[i],sim.f[i].value) for i in range(npx)] # dynamic load storage 260 | sim_hstor = np.append(sim_hstor,sim.h.value) # height of fluid in annulus storage 261 | sim_q_ins= np.append(sim_q_ins,sim.q_in.value) # reservoir influx storage 262 | sim_q_outs = np.append(sim_q_outs,sim.q_out.value) # production rate storage 263 | sim_P_ress = np.append(sim_P_ress,sim.P_res.value) # reservoir pressure storage 264 | sim_Vps = np.append(sim_Vps,sim.Vp.value) # cumulative volume produced storage 265 | sim_NPVs = np.append(sim_NPVs,sim.NPV.value) # NPV storage 266 | sim_W_rods = np.append(sim_W_rods,sim.W_rod.value) # work of rod (work to lift fluid) storage 267 | sim_SPMs = np.append(sim_SPMs,sim.SPM_in.value) # Strokes per minute storage 268 | sim_SPMr = np.append(sim_SPMr,sim.SPM.value) #Strokes per minute storage 269 | sim_thetas = np.append(sim_thetas,sim.theta.value) 270 | sim_P_wfs = np.append(sim_P_wfs,sim.P_wf.value) # bottom hole pressure storage 271 | sim_ys = np.append(sim_ys,sim.y.value) # sign of du/dt storage 272 | 273 | 274 | # ON/OFF control with timer 275 | 276 | h_meas = np.array(sim.h.value) 277 | h_min = np.min(h_meas) 278 | 279 | trigger = trigger + 1 280 | if trigger >30: 281 | sim.SPM_in.value = 15 282 | 283 | if h_min < 3597: 284 | sim.SPM_in.value = 0.0 285 | trigger = 0 286 | 287 | if trigger<30: 288 | sim.SPM_in.value = 0.0 289 | 290 | ####################################################################### 291 | 292 | # Plotting 293 | plt.clf() 294 | ax=plt.subplot(311) 295 | ax.grid() 296 | plt.plot(sim_ts[0:i*npt],sim_SPMs[0:i*npt],'ro',label='Motor Torque') 297 | plt.plot(sim_ts[0:i*npt],sim_SPMr[0:i*npt],'bo',label='SPM') 298 | plt.ylabel('Strokes per Minute') 299 | plt.legend(loc=2) 300 | ax=plt.subplot(312) 301 | ax.grid() 302 | plt.plot(sim_ts[0:i*npt],sim_hstor[0:i*npt],'k-',label= 'height') 303 | plt.plot(sim_ts[0:i*npt], np.ones(i*npt)*(sim.TVD.value*3/4 -3), label = 'height SP') 304 | #plt.plot(ts[0:i*npt], mpc_hs[0:npt*i], label = 'mpc height') 305 | plt.ylabel('Annular Fluid Height') 306 | plt.legend(loc='best') 307 | ax = plt.subplot(313) 308 | ax.grid() 309 | plt.plot(sim_ts[0:i*npt], sim_q_outs[0:i*npt], label = 'q_out') 310 | plt.plot(sim_ts[0:i*npt], sim_q_ins[0:i*npt], label = 'q_in') 311 | plt.legend() 312 | plt.ylabel('Flow Rate, STB/s') 313 | plt.xlabel('Time (sec)') 314 | plt.draw() 315 | 316 | plt.legend() 317 | plt.ylabel('Annular Fluid Height') 318 | plt.xlabel('Time (sec)') 319 | plt.draw() 320 | plt.pause(0.02) 321 | 322 | # store in dictionary 323 | #res['solve_stat' ] = solve_stat 324 | res['sim_ts' ] = sim_ts 325 | res['sim_us' ] = sim_us 326 | res['sim_vs' ] = sim_vs 327 | res['sim_fs' ] = sim_fs 328 | res['sim_hstor' ] = sim_hstor 329 | res['sim_q_ins' ] = sim_q_ins 330 | res['sim_q_outs' ] = sim_q_outs 331 | res['sim_P_ress' ] = sim_P_ress 332 | res['sim_Vps' ] = sim_Vps 333 | res[ 'NPVs' ] = sim_NPVs 334 | res['sim_W_rods' ] = sim_W_rods 335 | res['sim_SPMs' ] = sim_SPMs 336 | res['sim_SPMr' ] = sim_SPMr 337 | res['sim_thetas' ] = sim_thetas 338 | res['sim_P_wfs' ] = sim_P_wfs 339 | res['sim_ys' ] = sim_ys 340 | 341 | #%% 342 | 343 | np.save('Timer_controller_' + str(loops) + 's21npt.npy', res) 344 | 345 | #%% 346 | 347 | sim_TVD_value = 4800 # this is a little weird and affects line 361 as well 348 | res = np.load('Timer_controller_180s21npt.npy').item() 349 | 350 | fig = plt.figure() 351 | plt.rcParams['xtick.labelsize'] = 12 352 | plt.rcParams['ytick.labelsize'] = 12 353 | 354 | ax=plt.subplot(311) 355 | ax.grid() 356 | plt.plot(res['sim_ts'], res['sim_SPMs'], '--r', label=r'$T_{net}$ (ft-lb)') #r'Motor Torque ' for latex 357 | plt.plot(res['sim_ts'], res['sim_SPMr'], '-b', label='SPM') 358 | plt.ylabel('SPM', fontsize = 12) 359 | plt.legend(loc=2, fontsize = 12) 360 | plt.xlim(0,180) 361 | ax=plt.subplot(312) 362 | ax.grid() 363 | plt.plot(res['sim_ts'], res['sim_hstor'],'k-',label= 'Actual') 364 | plt.plot(res['sim_ts'], np.ones(np.size(res['sim_ts']))*(sim_TVD_value*3/4 -3), label = 'SP') # fix 365 | #plt.plot(ts[0:i*npt], mpc_hs[0:npt*i], label = 'mpc height') 366 | plt.ylabel('Fluid Level (ft)', fontsize = 12) 367 | plt.legend(loc='best', fontsize = 12) 368 | plt.xlim(0,180) 369 | ax = plt.subplot(313) 370 | ax.grid() 371 | plt.plot(res['sim_ts'], res['sim_q_outs'], label = r'$q_{out}$') 372 | plt.plot(res['sim_ts'], res['sim_q_ins'], label = r'$q_{in}$') 373 | plt.legend(loc='best', fontsize = 12) 374 | plt.xlim(0,180) 375 | plt.ylabel('Flow (STB/s)', fontsize = 12) 376 | plt.xlabel('Time (seconds)', fontsize = 12) 377 | plt.draw() 378 | 379 | plt.legend(loc='best', fontsize = 12) 380 | plt.xlim(0,180) 381 | plt.ylabel('Flow (STB/s)', fontsize = 12) 382 | plt.xlabel('Time (seconds)', fontsize = 12) 383 | 384 | plt.tight_layout() 385 | fig.savefig('Timer_control.eps', dpi = 1200, Transparent = True) 386 | plt.show() 387 | 388 | ##%% old graph 389 | # 390 | #plt.figure() 391 | # 392 | #ax=plt.subplot(311) 393 | #ax.grid() 394 | #plt.plot(sim_ts[0:i*npt],sim_SPMs[0:i*npt],'ro',label='Motor Torque') 395 | #plt.plot(sim_ts[0:i*npt],sim_SPMr[0:i*npt],'bo',label='SPM') 396 | #plt.ylabel('Strokes per Minute') 397 | #plt.legend(loc=2) 398 | #ax=plt.subplot(312) 399 | #ax.grid() 400 | #plt.plot(sim_ts[0:i*npt],sim_hstor[0:i*npt],'k-',label= 'height') 401 | #plt.plot(sim_ts[0:i*npt], np.ones(i*npt)*(sim.TVD.value*3/4 -3), label = 'height SP') 402 | ##plt.plot(ts[0:i*npt], mpc_hs[0:npt*i], label = 'mpc height') 403 | #plt.ylabel('Annular Fluid Height') 404 | #plt.legend(loc='best') 405 | #ax = plt.subplot(313) 406 | #ax.grid() 407 | #plt.plot(sim_ts[0:i*npt], sim_q_outs[0:i*npt], label = 'q_out') 408 | #plt.plot(sim_ts[0:i*npt], sim_q_ins[0:i*npt], label = 'q_in') 409 | #plt.legend() 410 | #plt.ylabel('Flow Rate, STB/s') 411 | #plt.xlabel('Time (sec)') 412 | #plt.draw() 413 | # 414 | #plt.legend() 415 | #plt.ylabel('Annular Fluid Height') 416 | #plt.xlabel('Time (sec)') 417 | # 418 | #plt.show() 419 | 420 | 421 | 422 | 423 | -------------------------------------------------------------------------------- /MHE_PI.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Apr 6 13:20:42 2018 4 | 5 | @author: Brandon 6 | """ 7 | 8 | #from __future__ import division # compatibility with python 2.7 9 | from gekko import GEKKO 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | import time 13 | from mpl_toolkits.mplot3d.axes3d import Axes3D 14 | #%% 15 | #Define Timespace for simulation 16 | tf = 1 # sec, length of simulation 17 | npt = 20 # number of time discretizations 18 | nit = 1 # number of itterations to solve 19 | ti = np.linspace(0,tf,npt) # times for plotting 20 | 21 | 22 | # Define time space for MPC 23 | tf_mpc = 5 24 | npt_mpc = npt*tf_mpc 25 | 26 | 27 | 28 | # Define Rod Discretizations 29 | TVD_d = 4800 # ft, lenth of rod 30 | npx = 10 # number of rod discretizations 31 | dx = TVD_d/(npx-1) # #ft lenth of rod discretizations 32 | xi = np.linspace(0,TVD_d,npx) # possitions allong rod (for plotting) 33 | 34 | #Set Points 35 | SPM_in = np.ones(npt)*10 36 | 37 | 38 | #BuildModle#################################################################### 39 | sim = GEKKO() 40 | mhe = GEKKO() 41 | 42 | #Horizon Window 43 | sim.time = np.linspace(0,tf,npt) 44 | mhe.time = np.linspace(0,tf,npt) 45 | 46 | ################################ 47 | # Conventional Rod Pump Unit Geometry 48 | # API geometry dimension values 49 | Ag=210.0 50 | Cg=120.3 51 | Ig=120.0 52 | Pg=148.5 53 | Hg=237.88 54 | Gg=86.88 55 | Rg=47.0 56 | 57 | #lengths from FIG. 1 - Beam Pumping Unit Shown as a Four-Bar Linkage 58 | L_1 = Rg 59 | L_2 = np.sqrt((Hg-Gg)**2.0+Ig**2.0) 60 | L_3 = Cg 61 | L_4 = Pg 62 | L_5 = Ag 63 | 64 | starting_height = 3/4 65 | 66 | #Setpoints 67 | level_height = 3 68 | SP = starting_height*TVD_d-level_height 69 | dSP = 0.3 70 | 71 | #Simulation######################################## 72 | for m in [sim,mhe]: 73 | #Constants 74 | m.API = m.Const(value = 45) #API gravity of fluid, unitless 75 | m.c = m.Const(value = 0.000013) #Compressibility, psi^-1 76 | m.k = m.Const(value = 15) #Permeability, md 77 | m.Bo = m.Const(value = 1.2) #FVF, rb/STB 78 | m.A_d = m.FV(value = 2, ub = 8, lb = 1) #Drainage Area, Acres 79 | m.sw = m.Const(value = 0.2) #Water Saturation 80 | m.porosity = m.FV(value = 0.08, ub = .12, lb = 0.07) #Porosity, unitless 81 | m.gamma_E = m.Const(value = 1.78) #Euler Constant 82 | m.C_a = m.Const(value = 31.6) #Drainage Area Shape Factor (Circular) 83 | m.rw = m.Const(value = 0.328) #Welbore radius, ft 84 | m.S = m.FV(value = 0, ub = 10, lb = -5) #unitless 85 | m.u_visc = m.Const(value = 1.5) # Viscosity, cp 86 | m.h_pz = m.Const(value = 8) #pay zone thickness, ft 87 | m.D_t = m.Const(value = 2.5) # tubing diameter, in 88 | m.St_length = m.Const(value = 85) # rod pump stroke length, in 89 | 90 | m.g = m.Const(value = 32.2) # acceleration due to gravity, ft/s^3 91 | m.g_conv= m.Const(value = 32.2) # lbf conversion , lb-ft/s^2-lbf 92 | m.rho_r = m.Const(value = 490) # lbs/ft^3, density of rod steel 93 | m.rho_w = m.Const(value = 62.3 ) # lbs/ft^3, density of water at standard conditions 94 | m.a = m.Const(value =18996.06 ) # ft/s speed of sound in steel 95 | m.D_r = m.Const(value = 1.0) # in, diameter of rod string 96 | m.Ac = m.Const(value= m.D_r.value**2/4.0*np.pi) # in^2, cross sectional area of rod 97 | m.nu = m.Const(value = 0.01) # unitless, damping coefficient 98 | m.pi = m.Const(value=np.pi) 99 | m.E = m.Const(value = 32025000.0) # psi sucker rod modulus of elasticity 100 | m.alpha = m.Const(value = 0.0) # pump parameter, unitless 101 | m.beta = m.Const(value = 1.0) # pump parameter, unitless 102 | 103 | m.L_1 = m.Const(value =L_1) # unit geometry 104 | m.L_2 = m.Const(value =L_2) # unit geometry 105 | m.L_3 = m.Const(value =L_3) # unit geometry 106 | m.L_4 = m.Const(value =L_4) # unit geometry 107 | m.L_5 = m.Const(value =L_5) # unit geometry 108 | m.dx = m.Const(value = dx) # ft delta x 109 | 110 | #Prime Mover Constants (Torque Balance) 111 | m.tau_p = m.Const(value = 3) #tau 112 | m.k_gain = m.Const(value = 1) #one to one ratio between torque and SPM 113 | 114 | ##Economic 115 | m.Weight_lb_ft = m.Const(value = m.rho_r.value*m.Ac.value*m.g.value/m.g_conv/144) #Weight of rod string, lbf/ft 116 | m.Capex = m.Const(value = 200000) #Cost of Pumping Rod Unit,$? 117 | m.P_o = m.Const(value = 50) #Price of Oil, $/STB 118 | m.r = m.Const(value= .12/365) #Daily Discount Rate, % 119 | m.P_th = m.Const(value = 100) #tubing head pressure, psi 120 | m.TVD = m.Const(value = 4800) #true vertical depth, ft 121 | m.E_cost = m.Const(value = 0.13/3600) #Cost of Electricity, cents/Kws 122 | 123 | #Calculated Constants #DO NOT MODIFY# 124 | m.Wr = m.Const(value = m.TVD.value*m.Weight_lb_ft.value) #Weight of entire rod string, lbm 125 | m.D_a = m.Const(value = 2*12*m.rw.value) #Annulus Diameter, in 126 | m.gamma = m.Const(141.5/(m.API.value+131.5)) #Specific gravity of Fluid 127 | m.P_startpump = m.Const(value = 0.433*m.gamma.value*m.TVD.value) #Average Reservoir Pressure at Pump start up 128 | m.Pi = m.Const(value = .433*m.TVD.value) #Initial Reservoir Pressure, psi 129 | 130 | m.A_t = m.Const((np.pi/4)*m.D_t.value**2) #Cross sectional Area of tubing, in^2 131 | m.A_a = m.Const((np.pi/4)*m.D_a.value**2) #Cross Sectional Area of Annulus, in^2 132 | m.Wf = m.Const(value = m.TVD.value*m.rho_w.value*m.gamma.value*m.g.value/m.g_conv.value*(m.A_t.value-m.Ac.value)/144) # lbf, weight of fluid in tubing 133 | 134 | #MV's 135 | m.SPM_in = m.MV(value = 15, lb = 5, ub = 15) #Rod Pump Pumping Speed/Torque, spm 136 | 137 | #Variables 138 | m.V_i= m.Var(value = 7758*m.A_d.value*m.h_pz.value*m.porosity.value*(1-m.sw.value)/m.Bo.value) #OOIP, stb 139 | m.Vp = m.Var(value = m.V_i.value*(np.exp(m.c.value*(m.Pi.value-m.P_startpump.value))-1)) #initial volume produced prior stb 140 | 141 | if m == sim: 142 | m.h = m.CV(value = 1.0*m.TVD.value*starting_height) 143 | else: 144 | m.h = m.MV(value = 1.0*m.TVD.value*starting_height, lb = 0, ub = 4800) # Height, ft 145 | 146 | m.NPV = m.Var(value = -1.0*m.Capex.value) #Net Present Value, $ 147 | m.y = m.Var( lb = -1, ub = 1) # SIGN(x) 148 | m.sa = m.Var(value = 0, lb = 0) # slack variable a 149 | m.sb = m.Var(value = 0, lb = 0) # slack variable b 150 | m.tsi = m.Var(value = 0.0) # mulation time 151 | m.SPM = m.Var(value = 15) #SPM, strokes/min 152 | #omega = m.Var(value = 0) 153 | m.theta = m.Var(value = 0) # rad i.e sec^-1 crank angle of surface unit 154 | m.u = [m.SV(value = 9.22) for i in range(npx)] # relative position of each rod segment 155 | m.v = [m.Var(value = 0.0) for i in range(npx)] # velocity of reach rod segment 156 | m.f = [m.SV(value = 0.0) for i in range (npx)] # load at each rod segment 157 | m.P = m.Var(value = 1e-6) # unitless, load at the pump 158 | 159 | 160 | ## State Variables 161 | m.P_res = m.Var(value = m.P_startpump.value*1.0) #Current Reservoir Pressure , psi 162 | m.P_wf = m.Var(value = 0.433*m.gamma*m.h.value) #Bottomhole Flowing Pressure, psi 163 | m.q_in = m.Var(value = (1/86400)*m.k.value*m.h_pz.value*(m.P_res.value-m.P_wf.value)/(141.2*m.Bo.value*m.u_visc.value*((1/2)*np.log(4*m.A_d.value/(m.gamma_E.value*m.C_a.value*m.rw.value**2)) + m.S.value))) #IPR-VLP Flow rate, STB/s 164 | m.q_out = m.Var(value = 0) # Outgoing Flow Rate, STB/s 165 | m.t = m.Var(value = 0) #Time, days 166 | m.W_rod = m.Var(value = (1.0962)*m.q_out.value*(m.P_th.value-m.P_wf.value + .433*m.gamma.value*m.TVD.value) + (4.7053e-7)*m.Wr.value*m.St_length.value*m.SPM.value) #Work supplied by electric Motor, KW 167 | 168 | #Intermediates 169 | m.hs = m.Intermediate(m.sqrt(L_1**2 +L_2**2 + 2 *L_1 *L_2 *m.cos(m.theta))) 170 | 171 | #Equations 172 | ##AlgebraicEqns 173 | m.Equation(m.V_i == 7758*m.A_d*m.h_pz*m.porosity*(1-m.sw)/m.Bo) 174 | m.Equation(m.P_wf == 0.433*m.gamma*m.h) 175 | m.Equation(m.P_res == m.Pi-(1/m.c)*m.log((m.Vp/m.V_i)+1)) 176 | m.Equation(m.q_in == (1/86400)*m.k*m.h_pz*(m.P_res-m.P_wf)/(141.2*m.Bo*m.u_visc*((1/2)*m.log(4*m.A_d/(m.gamma_E*m.C_a*m.rw**2)) + m.S))) #STB/s 177 | m.Equation(m.W_rod == (1.0962)*m.q_out*(m.P_th-m.P_wf + .433*m.gamma*m.TVD) + (4.7053e-7)*m.Wr*m.St_length*m.SPM) 178 | 179 | #Prime Mover Equations- Torque Balance and Kinematic Eqns 180 | m.Equation(m.SPM.dt() == -(1/m.tau_p)*m.SPM + (m.k_gain/m.tau_p)*m.SPM_in) 181 | m.Equation((2*m.pi/60)*m.SPM == m.theta.dt()) 182 | 183 | m.Equation(m.u[0] == (1/12)*L_5*(m.asin(L_1*m.sin(m.theta)/m.hs)+m.acos((m.hs**2+L_3**2-L_4**2)/(2*L_3*m.hs)))) # position of polished rod, inches 184 | [m.Equation(m.v[i+1].dt()== m.a**2 * (m.u[i+2] - 2.0*m.u[i+1] + m.u[i])/m.dx**2 - m.pi*m.a*m.nu/(2.0*m.TVD)*m.v[i+1] - (1-m.rho_w*m.gamma/m.rho_r)*m.g) for i in range(npx-2) ]# wave equation 185 | m.Equation(m.q_out == m.A_t * m.u[-1].dt()*12/231/42 * (1+m.y)/2) # rate of fluid production, barrels/ 186 | 187 | # Equations for calculating rod loading 188 | # Load at surface 189 | m.Equation(m.f[0] == m.E*m.Ac*1/2/m.dx *(-m.u[2] + 4*m.u[1] -3*m.u[0])) 190 | # Load at pump 191 | m.Equation(m.f[npx-1] == m.E*m.Ac* m.P) 192 | # load at intermediate points 193 | [m.Equation(m.f[1+i] == m.E*m.Ac*1/2.0/dx*(m.u[i+2] - m.u[i])) for i in range(npx-2)] 194 | # pump boundary 195 | m.Equation( m.u[npx-1]*m.alpha + (m.u[npx-1] - m.u[npx-2])/dx == m.P) 196 | #add in signum for lifting and lowering conditions 197 | m.Equation(m.v[-1] == m.sb - m.sa ) 198 | m.Equation(m.P == -((m.Wf- (m.A_t - m.Ac)*m.P_wf)/m.E/m.Ac) * (1 + m.y)/2 ) # -P_wf*A_t 199 | 200 | 201 | ##DifferentialEans 202 | m.Equation(m.t.dt() == 1) 203 | m.Equation(m.Vp.dt() == m.q_in) 204 | m.Equation(m.NPV.dt() == (m.P_o*m.q_out-m.E_cost*m.W_rod)*m.exp(-m.r*m.t)) 205 | m.Equation(m.h.dt() == (1617/2)*(m.q_in - m.q_out)/(m.A_a -m.A_t)) 206 | m.Equation(m.tsi.dt()==1.0) # create time variable 207 | [m.Equation(m.u[i].dt()==m.v[i]) for i in range(npx)] # velocity of rod string 208 | 209 | # Set Objectives ################################################## 210 | m.Obj((m.sa*(1+m.y) + m.sb*(1-m.y))) # objective function to make signum work. 211 | 212 | #SetGlobalOptions(Simulation)############################################################## 213 | sim.options.IMODE = 5 # 4 = Dynamic Simulation (Seqential) 214 | sim.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 215 | sim.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 216 | sim.options.time_shift = npt-1 # time shift forward for multiple simulations 217 | sim.options.MAX_ITER = 450 218 | 219 | #SetLocalOptions############################################################### 220 | #N/A 221 | sim.SPM_in.FSTATUS = 1 # accept measurments 222 | sim.SPM_in.STATUS = 0 # don't let optimizer change (simulation) 223 | 224 | #MHE########################################################################### 225 | #Parameters (Holds Measured values from MHE) 226 | fm = mhe.Param(value = sim.f[0]) 227 | 228 | #SetGlobalOptions(MHE)########################################################## 229 | mhe.options.IMODE = 5 # 4 = Dynamic Simulation (Seqential) 230 | mhe.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 231 | mhe.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 232 | mhe.options.time_shift = npt-1 # time shift forward for multiple simulations 233 | mhe.options.MAX_ITER = 700 234 | mhe.Obj((mhe.f[0] - fm)**2) 235 | 236 | #SetLocalOptions (MHE)############################################################### 237 | ##FV #Variable to estimate 238 | mhe.h.FSTATUS = 0 239 | mhe.h.STATUS = 1 240 | mhe.h.DMAX = 0.5 241 | 242 | #MV 243 | mhe.SPM_in.FSTATUS = 1 244 | mhe.SPM_in.STATUS = 0 245 | 246 | #Solve######################################################################### 247 | #%% 248 | # Solve the simulation in a loop to simulate a longer horizon 249 | loops = 180 # number of steps forward in time 250 | 251 | res = {} 252 | solve_stat = np.zeros(loops) 253 | t_cycle = 0 254 | #PID Options ###################################################### 255 | e = np.zeros(loops) 256 | ie = np.zeros(loops) 257 | op = np.zeros(loops) 258 | 259 | #SPM_output 260 | op[0] = np.ones(1)*sim.SPM_in.value 261 | ophi = 15 262 | oplo = 5 263 | 264 | pv = np.zeros(loops) 265 | P = np.zeros(loops) 266 | I = np.zeros(loops) 267 | pv_ave = np.zeros(loops) 268 | 269 | #Tuning 270 | kc = 10 271 | tauI = 5 272 | 273 | ################################################################### 274 | #Initialize Storage Values 275 | sim_ts = np.ones(npt)*sim.tsi.value # simulation time storage 276 | sim_hstor = np.ones(npt)*sim.h.value # height of fluid in annulus storage 277 | sim_q_ins= np.ones(npt)*sim.q_in.value # reservoir influx storage 278 | sim_q_outs = np.ones(npt)*sim.q_out.value # production rate storage 279 | sim_P_ress = np.ones(npt)*sim.P_res.value # reservoir pressure storage 280 | sim_Vps = np.ones(npt)*sim.Vp.value # cumulative volume produced storage 281 | sim_NPVs = np.ones(npt)*sim.NPV.value # NPV storage 282 | sim_W_rods = np.ones(npt)*sim.W_rod.value # work of rod (work to lift fluid) storage 283 | sim_SPMs = np.ones(npt)*sim.SPM_in.value # Strokes per minute/ Torque storage Set Points 284 | sim_SPMr = np.ones(npt)*sim.SPM.value #SPM storage 285 | sim_thetas = np.ones(npt)*sim.theta.value#Theta storage 286 | sim_P_wfs = np.ones(npt)*sim.P_wf.value # bottom hole pressure storage 287 | sim_ys = np.ones(npt)*sim.y.value # sign of du/dt storage 288 | 289 | 290 | #MHE Storage 291 | mpc_ts = np.empty(0) # simulation time storage 292 | mhe_us = [np.array(mhe.u[i].value) for i in range(npx)] # u relative position storage 293 | mhe_vs = [np.array(mhe.v[i].value) for i in range(npx)] 294 | mhe_fs = [np.array(mhe.f[i].value) for i in range(npx)] # dynamic load storage 295 | mpc_hstor = np.empty(0)# height of fluid in annulus storage 296 | mpc_q_ins= np.empty(0) # reservoir influx storage 297 | mpc_q_outs = np.empty(0) # production rate storage 298 | mpc_P_ress = np.empty(0) # reservoir pressure storage 299 | mpc_Vps = np.empty(0) # cumulative volume produced storage 300 | mpc_NPVs = np.empty(0) # NPV storage 301 | mpc_W_rods =np.empty(0) # work of rod (work to lift fluid) storage 302 | mpc_SPMs = np.empty(0) # Strokes per minute/ Torque storage Set Points 303 | mpc_SPMr = np.empty(0) #SPM storage 304 | mpc_thetas = np.empty(0)#Theta storage 305 | mpc_P_wfs = np.empty(0) # bottom hole pressure storage 306 | mpc_ys = np.empty(0) # sign of du/dt storage 307 | mpc_Skins = np.empty(0) #Skin storage 308 | mpc_porosity = np.empty(0) 309 | mpc_A_d = np.empty(0) 310 | mhe_Skins = np.empty(0) #Skin storage 311 | mhe_hstor = np.empty(0)# height of fluid in annulus storage 312 | mpc_Skins = np.empty(0) 313 | mpc_Skinss = np.empty(0) 314 | mpc_h = np.empty(0) 315 | mpc_hss = np.empty(0) 316 | ############################################################### 317 | 318 | for i in range(loops): 319 | 320 | # simulate system for 1 second 321 | sim.solve() 322 | if i == 0: 323 | # Create and store results 324 | sim_ts = np.array(sim.tsi.value) # simulation time storage 325 | sim_us = [np.array(sim.u[i].value) for i in range(npx)] # u relative position storage 326 | sim_vs = [np.array(sim.v[i].value) for i in range(npx)] 327 | sim_fs = [np.array(sim.f[i].value) for i in range(npx)] # dynamic load storage 328 | sim_hstor = np.array(sim.h.value) # height of fluid in annulus storage 329 | sim_q_ins= np.array(sim.q_in.value) # reservoir influx storage 330 | sim_q_outs = np.array(sim.q_out.value) # production rate storage 331 | sim_P_ress = np.array(sim.P_res.value) # reservoir pressure storage 332 | sim_Vps = np.array(sim.Vp.value) # cumulative volume produced storage 333 | sim_NPVs = np.array(sim.NPV.value) # NPV storage 334 | sim_W_rods = np.array(sim.W_rod.value) # work of rod (work to lift fluid) storage 335 | sim_SPMs = np.array(sim.SPM_in.value) # Strokes per minute/ Torque storage Set Points 336 | sim_SPMr = np.array(sim.SPM.value) #SPM storage 337 | sim_thetas = np.array(sim.theta.value)#Theta storage 338 | sim_P_wfs = np.array(sim.P_wf.value) # bottom hole pressure storage 339 | sim_ys = np.array(sim.y.value) # sign of du/dt storage 340 | 341 | elif i>0: 342 | sim_ts = np.append(sim_ts,sim.tsi.value) # simulation time storage 343 | sim_us = [np.append(sim_us[i],sim.u[i].value) for i in range(npx)] # u relative position storage 344 | sim_vs = [np.append(sim_vs[i],sim.v[i].value) for i in range(npx)] 345 | sim_fs = [np.append(sim_fs[i],sim.f[i].value) for i in range(npx)] # dynamic load storage 346 | sim_hstor = np.append(sim_hstor,sim.h.value) # height of fluid in annulus storage 347 | sim_q_ins= np.append(sim_q_ins,sim.q_in.value) # reservoir influx storage 348 | sim_q_outs = np.append(sim_q_outs,sim.q_out.value) # production rate storage 349 | sim_P_ress = np.append(sim_P_ress,sim.P_res.value) # reservoir pressure storage 350 | sim_Vps = np.append(sim_Vps,sim.Vp.value) # cumulative volume produced storage 351 | sim_NPVs = np.append(sim_NPVs,sim.NPV.value) # NPV storage 352 | sim_W_rods = np.append(sim_W_rods,sim.W_rod.value) # work of rod (work to lift fluid) storage 353 | sim_SPMs = np.append(sim_SPMs,sim.SPM_in.value) # Strokes per minute storage 354 | sim_SPMr = np.append(sim_SPMr,sim.SPM.value) #Strokes per minute storage 355 | sim_thetas = np.append(sim_thetas,sim.theta.value) 356 | sim_P_wfs = np.append(sim_P_wfs,sim.P_wf.value) # bottom hole pressure storage 357 | sim_ys = np.append(sim_ys,sim.y.value) # sign of du/dt storage 358 | solve_stat[i] = t_cycle 359 | ##MHE################################################################## 360 | #Insert Measurements 361 | fm.value = sim.f[0].value 362 | 363 | #Insert move 364 | mhe.SPM_in.value = sim.SPM_in.value 365 | 366 | #Solve 367 | t_start = time.time() 368 | mhe.solve() 369 | 370 | #Pass values to MPC 371 | pv[i] = mhe.h.NEWVAL 372 | pv_ave[i] = np.average(pv[i-10:i]) 373 | #Store new values for plotting 374 | mhe_hstor = np.append(mhe_hstor,mhe.h.value) 375 | 376 | # ##PID ##################################################################### 377 | # if i<10: 378 | e[i] = pv[i] - SP 379 | # if i>10: 380 | # e[i] = pv_ave[i] - SP 381 | # 382 | # if i >= 1: 383 | ie[i] = ie[i-1] + e[i]*tf 384 | 385 | P[i] = kc * e[i] 386 | I[i] = kc/tauI * ie[i] 387 | 388 | op[i] = op[0] + P[i] + I[i] 389 | 390 | #Anti-reset Wind up 391 | if op[i] > 15: 392 | op[i] = ophi 393 | ie[i] = ie[i] - e[i]*tf 394 | if op[i] < 5: 395 | op[i] = oplo 396 | ie[i] = ie[i] - e[i]*tf 397 | 398 | #Pass output to simulation 399 | t_end = time.time() 400 | t_cycle = t_end - t_start 401 | sim.SPM_in.value = np.ones(npt)*op[i] 402 | 403 | # ####################################################################### 404 | # 405 | # Plotting 406 | plt.clf() 407 | ax=plt.subplot(311) 408 | ax.grid() 409 | plt.plot(sim_ts[0:i*npt],sim_SPMs[0:i*npt],'ro',label='SPM Set Point') 410 | plt.plot(sim_ts[0:i*npt],sim_SPMr[0:i*npt],'bo',label='SPM') 411 | plt.ylabel('Strokes per Minute') 412 | plt.legend(loc=2) 413 | ax=plt.subplot(312) 414 | ax.grid() 415 | plt.plot(sim_ts[0:i*npt],sim_hstor[0:i*npt],'k-',label= 'Height') 416 | plt.plot(sim_ts[0:i*npt], np.ones(i*npt)*SP, label = 'height SP') 417 | #plt.plot(ts[0:i*npt], mpc_hs[0:npt*i], label = 'mpc height') 418 | plt.ylabel('Annular Fluid Height (ft)') 419 | plt.legend(loc='best') 420 | ax = plt.subplot(313) 421 | ax.grid() 422 | plt.plot(sim_ts[0:i*npt], sim_q_outs[0:i*npt], label = 'q_out') 423 | plt.plot(sim_ts[0:i*npt], sim_q_ins[0:i*npt], label = 'q_in') 424 | plt.legend() 425 | plt.ylabel('Flow Rate, STB/s') 426 | plt.xlabel('Time (sec)') 427 | plt.draw() 428 | plt.pause(0.02) 429 | 430 | #%% 431 | 432 | res['solve_stat'] = solve_stat 433 | res['ts' ] = sim_ts 434 | res['us' ] = sim_us 435 | res['vs' ] = sim_vs 436 | res['fs'] = sim_fs 437 | res['hstor' ] = sim_hstor 438 | res[ 'q_ins' ] = sim_q_ins 439 | res[ 'q_outs' ] = sim_q_outs 440 | res['P_ress' ] = sim_P_ress 441 | res['Vps' ] = sim_Vps 442 | res[ 'NPVs' ] = sim_NPVs 443 | res['W_rods' ] = sim_W_rods 444 | res['SPMs'] = sim_SPMs 445 | res['SPMr'] = sim_SPMr 446 | res['thetas'] = sim_thetas 447 | res['P_wfs'] = sim_P_wfs 448 | res[ 'ys'] = sim_ys 449 | res['h_SP'] = np.ones(loops*npt)*SP 450 | 451 | np.save('PI_MHE_Control_Results_Aggresive_20npt_10npt.npy', res) 452 | #%% 453 | # Load dictionary of results 454 | res = np.load('PI_MHE_Control_Results_Aggresive_20npt_10npt.npy').item() 455 | 456 | #%% Plotting from dictionary 457 | 458 | 459 | plt.figure() 460 | plt.rcParams['xtick.labelsize'] = 12 461 | plt.rcParams['ytick.labelsize'] = 12 462 | ax=plt.subplot(311) 463 | ax.grid() 464 | plt.plot(res['ts'], res['SPMs'], 'r--', label=r'$T_{net}$ (ft-lb)')#, s = 4, c='b' ) # 'ro', for latex 465 | plt.plot(res['ts'], res['SPMr'],'b-', label=r'Actual')#, s = 4, c = 'r') #'bo', 466 | plt.ylabel('SPM', fontsize = 12) 467 | plt.legend(loc= 1,fontsize = 12) 468 | plt.xlim(0,180) 469 | ax=plt.subplot(312) 470 | ax.grid() 471 | plt.plot(res['ts'], res['hstor'],'k-',label= 'Actual') 472 | plt.plot(res['ts'], np.ones(np.size(res['ts']))*(sim.TVD.value*3/4 -3), label = 'SP') # fix 473 | #plt.plot(ts[0:i*npt], mpc_hs[0:npt*i], label = 'mpc height') 474 | plt.ylabel('Fluid Level (ft)', fontsize = 12) 475 | plt.legend(loc=1,fontsize = 12) 476 | plt.xlim(0,180) 477 | ax = plt.subplot(313) 478 | ax.grid() 479 | plt.plot(res['ts'], res['q_outs'], label = r'$q_{out}$') 480 | plt.plot(res['ts'], res['q_ins'], label = r'$q_{in}$') 481 | plt.legend(loc = 1,fontsize = 12) 482 | plt.ylabel('Flow (STB/s)', fontsize = 12) 483 | plt.xlabel('Time (seconds)', fontsize = 12) 484 | plt.xlim(0,180) 485 | plt.draw() 486 | 487 | #plt.legend(fontsize = 12) 488 | #plt.ylabel('Fluid Level (ft)', fontsize = 12) 489 | #plt.xlabel('Time (seconds)', fontsize = 12) 490 | 491 | plt.tight_layout() 492 | plt.savefig('PI_MHE_Control_K_10_Tau_5.eps', transparent = True, dpi = 1200) 493 | plt.show() 494 | #%% 495 | # timing figure 496 | plt.rcParams['xtick.labelsize'] = 12 497 | plt.rcParams['ytick.labelsize'] = 12 498 | plt.figure() 499 | plt.plot(np.linspace(1,180,179), res['solve_stat'][1:],'r-',label='Solve Time') 500 | plt.plot(np.linspace(1,loops,loops-1), np.ones(loops-1)*np.average(res['solve_stat'][1:]),'b--', label = 'Average') 501 | plt.plot(np.linspace(1,loops,loops-1), np.ones(loops-1),'k:', label = 'Real Time') 502 | plt.xlabel('Control Cycle', fontsize = 12) 503 | plt.ylabel('Computation Time (seconds)', fontsize = 12) 504 | plt.legend(fontsize = 12) 505 | plt.ylim(0,4) 506 | plt.xlim(0,180) 507 | plt.savefig('PI_MHE_Simulation_Timing.eps', dpi = 1200, transparent = True) 508 | plt.show() 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | #%% 525 | ## Figure 1 ## Height in the Annulus and incoming and outgoing flow rate#### 526 | plt.figure(1, figsize = (6,4.5)) 527 | plt.subplot(211) 528 | plt.plot(sim_ts, sim_hstor, 'r--', label = 'height in annulus') 529 | plt.plot(sim_ts, np.ones(len(sim_ts))*SP, 'b--', label = 'height Set Point') 530 | plt.ylabel('height, ft') 531 | plt.legend() 532 | 533 | plt.subplot(212) 534 | plt.plot(sim_ts, sim_q_ins, 'b--', label = r'$q_{in}$') 535 | plt.plot(sim_ts, sim_q_outs, 'g--', label = r'$q_{out}$') 536 | plt.ylabel('Flow Rate, STB/s') 537 | plt.xlabel('time, sec') 538 | plt.legend() 539 | 540 | plt.show() 541 | 542 | ##Figure 2: Reservoir Pressure Decline and Cumulative Volume Produced#### 543 | plt.figure(2, figsize = (6,4.5)) 544 | plt.subplot(211) 545 | plt.plot(sim_ts, sim_P_ress, 'k--', label = 'Reservoir Pressure') 546 | #plt.plot(m.time, P_wf.value, 'r--', label = r'$P_{wf}$') 547 | plt.ylabel('Pressure, psi') 548 | plt.legend() 549 | 550 | plt.subplot(212) 551 | plt.plot(sim_ts, sim_Vps, '--', label = 'Cumulative Volume Produced') 552 | plt.ylabel('Volume, STB') 553 | plt.xlabel('time, sec') 554 | plt.legend() 555 | plt.tight_layout() 556 | 557 | plt.show() 558 | 559 | ##Figure 3: NPV ######################################### 560 | plt.figure(3, figsize = (6,4.5)) 561 | plt.plot(sim_ts, sim_NPVs/(1e6), 'g:', label = 'NPV') 562 | plt.xlabel('time, sec') 563 | plt.ylabel('NPV, $ Millions') 564 | plt.legend() 565 | 566 | plt.show() 567 | 568 | ######################################################## 569 | 570 | #Figure 4# Work of Motor And 571 | plt.figure(4, figsize = (6,4.5)) 572 | plt.subplot(311) 573 | plt.plot(sim_ts,sim_W_rods, 'b-', label = 'Work Supplied by Motor' ) 574 | plt.ylabel('KiloWatts, KW') 575 | 576 | plt.subplot(312) 577 | plt.plot(sim_ts, sim_SPMs, 'r-', label = 'Input' ) 578 | plt.ylabel('SPM') 579 | 580 | plt.subplot(313) 581 | plt.plot(sim_ts, sim_P_wfs, 'r--', label = r'$P_{wf}$') 582 | plt.ylabel('FBHP, psi') 583 | plt.xlabel('time, sec') 584 | #plt.tight_layout() 585 | 586 | plt.show() 587 | 588 | ##Figure 5 -Doublet Test 589 | plt.figure(5, figsize = (6,4.5)) 590 | plt.subplot(211) 591 | plt.plot(sim_ts, sim_hstor, 'r--', label = 'height in annulus') 592 | plt.ylabel('height, ft') 593 | plt.legend() 594 | 595 | plt.subplot(212) 596 | plt.plot(sim_ts, sim_SPMs, 'b--', label = r'SPM') 597 | plt.ylabel('strokes/min') 598 | plt.xlabel('time, sec') 599 | plt.legend() 600 | 601 | plt.show() 602 | 603 | -------------------------------------------------------------------------------- /Fluid_Height_Estimation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Apr 6 13:20:42 2018 4 | 5 | @author: Brandon 6 | """ 7 | 8 | #from __future__ import division # compatibility with python 2.7 9 | from gekko import GEKKO 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | import time 13 | from mpl_toolkits.mplot3d.axes3d import Axes3D 14 | 15 | #Define Timespace 16 | tf = 1 # sec, length of simulation 17 | npt = 20 # number of time discretizations 18 | nit = 1 # number of itterations to solve 19 | ti = np.linspace(0,tf,npt) # times for plotting 20 | 21 | tf_mhe = 1 22 | 23 | # Define Rod Discretizations 24 | TVD_d = 4800 # ft, lenth of rod 25 | npx = 5 # number of rod discretizations 26 | 27 | dx = TVD_d/(npx-1) # #ft lenth of rod discretizations 28 | xi = np.linspace(0,TVD_d,npx) # possitions allong rod (for plotting) 29 | 30 | #Set Points 31 | SPM_in = np.ones(npt)*10 32 | 33 | 34 | #BuildModle#################################################################### 35 | mhe = GEKKO() 36 | sim = GEKKO() 37 | 38 | #Horizon Window 39 | sim.time = np.linspace(0,tf,npt) 40 | mhe.time = np.linspace(0,tf_mhe,npt*tf_mhe) 41 | 42 | 43 | ################################ 44 | # Conventional Rod Pump Unit Geometry 45 | # API geometry dimension values 46 | Ag=210.0 47 | Cg=120.3 48 | Ig=120.0 49 | Pg=148.5 50 | Hg=237.88 51 | Gg=86.88 52 | Rg=47.0 53 | 54 | #lengths from FIG. 1 - Beam Pumping Unit Shown as a Four-Bar Linkage 55 | L_1 = Rg 56 | L_2 = np.sqrt((Hg-Gg)**2.0+Ig**2.0) 57 | L_3 = Cg 58 | L_4 = Pg 59 | L_5 = Ag 60 | 61 | starting_height = 1/2 62 | #Simulation######################################## 63 | 64 | for m in [sim, mhe]: 65 | #Constants 66 | m.API = m.Const(value = 45) #API gravity of fluid, unitless 67 | m.c = m.Const(value = 0.000013) #Compressibility, psi^-1 68 | m.k = m.Const(value = 100) #Permeability, md 69 | m.Bo = m.Const(value = 1.2) #FVF, rb/STB 70 | m.A_d = m.FV(value = 2, ub = 8, lb = 1) #Drainage Area, Acres 71 | m.sw = m.Const(value = 0.2) #Water Saturation 72 | m.porosity = m.FV(value = 0.08, ub = .12, lb = 0.07) #Porosity, unitless 73 | m.gamma_E = m.Const(value = 1.78) #Euler Constant 74 | m.C_a = m.Const(value = 31.6) #Drainage Area Shape Factor (Circular) 75 | m.rw = m.Const(value = 0.328) #Welbore radius, ft 76 | m.S = m.FV(value = 6, ub = 10, lb = -5) #unitless 77 | m.u_visc = m.Const(value = 1.5) # Viscosity, cp 78 | m.h_pz = m.Const(value = 8) #pay zone thickness, ft 79 | m.D_t = m.Const(value = 2.5) # tubing diameter, in 80 | m.St_length = m.Const(value = 85) # rod pump stroke length, in 81 | 82 | m.g = m.Const(value = 32.2) # acceleration due to gravity, ft/s^3 83 | m.g_conv= m.Const(value = 32.2) # lbf conversion , lb-ft/s^2-lbf 84 | m.rho_r = m.Const(value = 490) # lbs/ft^3, density of rod steel 85 | m.rho_w = m.Const(value = 62.3 ) # lbs/ft^3, density of water at standard conditions 86 | m.a = m.Const(value =18996.06 ) # ft/s speed of sound in steel 87 | m.D_r = m.Const(value = 1.0) # in, diameter of rod string 88 | m.Ac = m.Const(value= m.D_r.value**2/4.0*np.pi) # in^2, cross sectional area of rod 89 | m.nu = m.Const(value = 0.01) # unitless, damping coefficient 90 | m.pi = m.Const(value=np.pi) 91 | m.E = m.Const(value = 32025000.0) # psi sucker rod modulus of elasticity 92 | m.alpha = m.Const(value = 0.0) # pump parameter, unitless 93 | m.beta = m.Const(value = 1.0) # pump parameter, unitless 94 | 95 | m.L_1 = m.Const(value =L_1) # unit geometry 96 | m.L_2 = m.Const(value =L_2) # unit geometry 97 | m.L_3 = m.Const(value =L_3) # unit geometry 98 | m.L_4 = m.Const(value =L_4) # unit geometry 99 | m.L_5 = m.Const(value =L_5) # unit geometry 100 | m.dx = m.Const(value = dx) # ft delta x 101 | 102 | #Prime Mover Constants (Torque Balance) 103 | m.tau_p = m.Const(value = 3) #tau 104 | m.k_gain = m.Const(value = 1) #one to one ratio between torque and SPM 105 | 106 | ##Economic 107 | m.Weight_lb_ft = m.Const(value = m.rho_r.value*m.Ac.value*m.g.value/m.g_conv/144) #Weight of rod string, lbf/ft 108 | m.Capex = m.Const(value = 200000) #Cost of Pumping Rod Unit,$? 109 | m.P_o = m.Const(value = 50) #Price of Oil, $/STB 110 | m.r = m.Const(value= .12/365) #Daily Discount Rate, % 111 | m.P_th = m.Const(value = 100) #tubing head pressure, psi 112 | m.TVD = m.Const(value = 4800) #true vertical depth, ft 113 | m.E_cost = m.Const(value = 0.13/3600) #Cost of Electricity, cents/Kws 114 | 115 | #Calculated Constants #DO NOT MODIFY# 116 | m.Wr = m.Const(value = m.TVD.value*m.Weight_lb_ft.value) #Weight of entire rod string, lbm 117 | m.D_a = m.Const(value = 2*12*m.rw.value) #Annulus Diameter, in 118 | m.gamma = m.Const(141.5/(m.API.value+131.5)) #Specific gravity of Fluid 119 | m.P_startpump = m.Const(value = 0.433*m.gamma.value*m.TVD.value) #Average Reservoir Pressure at Pump start up 120 | m.Pi = m.Const(value = .433*m.TVD.value) #Initial Reservoir Pressure, psi 121 | 122 | 123 | m.A_t = m.Const((np.pi/4)*m.D_t.value**2) #Cross sectional Area of tubing, in^2 124 | m.A_a = m.Const((np.pi/4)*m.D_a.value**2) #Cross Sectional Area of Annulus, in^2 125 | m.Wf = m.Const(value = m.TVD.value*m.rho_w.value*m.gamma.value*m.g.value/m.g_conv.value*(m.A_t.value-m.Ac.value)/144) # lbf, weight of fluid in tubing 126 | 127 | #MV's 128 | m.SPM_in = m.MV(value = 10, lb = 5, ub = 15) #Rod Pump Pumping Speed/Torque, spm 129 | 130 | #Variables 131 | m.V_i= m.Var(value = 7758*m.A_d.value*m.h_pz.value*m.porosity.value*(1-m.sw.value)/m.Bo.value) #OOIP, stb 132 | m.Vp = m.Var(value = m.V_i.value*(np.exp(m.c.value*(m.Pi.value-m.P_startpump.value))-1)) #initial volume produced prior stb 133 | 134 | if m == sim: 135 | m.h = m.Var(value = 1.0*m.TVD.value*starting_height) 136 | else: 137 | m.h = m.MV(value = 1.0*m.TVD.value*starting_height, lb = 0, ub = 3000) 138 | # Height, ft 139 | m.NPV = m.Var(value = -1.0*m.Capex.value) #Net Present Value, $ 140 | m.y = m.Var( lb = -1, ub = 1) # SIGN(x) 141 | m.sa = m.Var(value = 0, lb = 0) # slack variable a 142 | m.sb = m.Var(value = 0, lb = 0) # slack variable b 143 | m.tsi = m.Var(value = 0.0) # mulation time 144 | m.SPM = m.Var(value = 10) #SPM, strokes/min 145 | #omega = m.Var(value = 0) 146 | m.theta = m.Var(value = 0) # rad i.e sec^-1 crank angle of surface unit 147 | m.u = [m.SV(value = 9.22) for i in range(npx)] # relative position of each rod segment 148 | m.v = [m.Var(value = 0.0) for i in range(npx)] # velocity of reach rod segment 149 | m.f = [m.SV(value = 0.0) for i in range (npx)] # load at each rod segment 150 | m.P = m.Var(value = 1e-6) # unitless, load at the pump 151 | 152 | 153 | ## State Variables 154 | m.P_res = m.Var(value = m.P_startpump.value*1.0) #Current Reservoir Pressure , psi 155 | m.P_wf = m.Var(value = 0.433*m.gamma*m.h.value) #Bottomhole Flowing Pressure, psi 156 | m.q_in = m.Var(value = (1/86400)*m.k.value*m.h_pz.value*(m.P_res.value-m.P_wf.value)/(141.2*m.Bo.value*m.u_visc.value*((1/2)*np.log(4*m.A_d.value/(m.gamma_E.value*m.C_a.value*m.rw.value**2)) + m.S.value))) #IPR-VLP Flow rate, STB/s 157 | m.q_out = m.Var(value = 0) # Outgoing Flow Rate, STB/s 158 | m.t = m.Var(value = 0) #Time, days 159 | m.W_rod = m.Var(value = (1.0962)*m.q_out.value*(m.P_th.value-m.P_wf.value + .433*m.gamma.value*m.TVD.value) + (4.7053e-7)*m.Wr.value*m.St_length.value*m.SPM.value) #Work supplied by electric Motor, KW 160 | 161 | #Intermediates 162 | m.hs = m.Intermediate(m.sqrt(L_1**2 +L_2**2 + 2 *L_1 *L_2 *m.cos(m.theta))) 163 | m.V_rp = m.Intermediate((1/9702)*(np.pi/4)*m.D_t**2*m.St_length) #Volume Extracted per stroke length, STB 164 | 165 | #Equations 166 | ##AlgebraicEqns 167 | m.Equation(m.V_i == 7758*m.A_d*m.h_pz*m.porosity*(1-m.sw)/m.Bo) 168 | m.Equation(m.P_wf == 0.433*m.gamma*m.h) 169 | m.Equation(m.P_res == m.Pi-(1/m.c)*m.log((m.Vp/m.V_i)+1)) 170 | m.Equation(m.q_in == (1/86400)*m.k*m.h_pz*(m.P_res-m.P_wf)/(141.2*m.Bo*m.u_visc*((1/2)*m.log(4*m.A_d/(m.gamma_E*m.C_a*m.rw**2)) + m.S))) #STB/s 171 | m.Equation(m.W_rod == (1.0962)*m.q_out*(m.P_th-m.P_wf + .433*m.gamma*m.TVD) + (4.7053e-7)*m.Wr*m.St_length*m.SPM) 172 | 173 | #Prime Mover Equations- Torque Balance and Kinematic Eqns 174 | m.Equation(m.SPM.dt() == -(1/m.tau_p)*m.SPM + (m.k_gain/m.tau_p)*m.SPM_in) 175 | m.Equation((2*m.pi/60)*m.SPM == m.theta.dt()) 176 | 177 | #m.Equation(theta ==2.5118541087922712 +tsi*SPM_in * 2.0 * 3.147 / 60.0) # convert time to angle in radians 178 | #m.Equation(SPM == omega/(2*pi)/60) 179 | 180 | m.Equation(m.u[0] == (1/12)*L_5*(m.asin(L_1*m.sin(m.theta)/m.hs)+m.acos((m.hs**2+L_3**2-L_4**2)/(2*L_3*m.hs)))) # position of polished rod, inches 181 | [m.Equation(m.v[i+1].dt()== m.a**2 * (m.u[i+2] - 2.0*m.u[i+1] + m.u[i])/m.dx**2 - m.pi*m.a*m.nu/(2.0*m.TVD)*m.v[i+1] - (1-m.rho_w*m.gamma/m.rho_r)*m.g) for i in range(npx-2) ]# wave equation 182 | m.Equation(m.q_out == m.A_t * m.u[-1].dt()*12/231/42 * (1+m.y)/2) # rate of fluid production, barrels/ 183 | #m.Equation(q_out == (1/60)*V_rp*SPM) 184 | 185 | # Equations for calculating rod loading 186 | # Load at surface 187 | m.Equation(m.f[0] == m.E*m.Ac*1/2/m.dx *(-m.u[2] + 4*m.u[1] -3*m.u[0])) 188 | # Load at pump 189 | #m.Equation(f[npx-1] == E*Ac* 1/2.0/dx *(3*u[npx-1] - 4*u[npx-2] + u[npx-3])) 190 | m.Equation(m.f[npx-1] == m.E*m.Ac* m.P) 191 | # load at intermediate points 192 | [m.Equation(m.f[1+i] == m.E*m.Ac*1/2.0/dx*(m.u[i+2] - m.u[i])) for i in range(npx-2)] 193 | # pump boundary 194 | m.Equation( m.u[npx-1]*m.alpha + (m.u[npx-1] - m.u[npx-2])/dx == m.P) 195 | #add in signum for lifting and lowering conditions 196 | m.Equation(m.v[-1] == m.sb - m.sa ) 197 | m.Equation(m.P == -((m.Wf- (m.A_t - m.Ac)*m.P_wf)/m.E/m.Ac) * (1 + m.y)/2 ) # -P_wf*A_t 198 | 199 | 200 | ##DifferentialEans 201 | m.Equation(m.t.dt() == 1) 202 | m.Equation(m.Vp.dt() == m.q_in) 203 | m.Equation(m.NPV.dt() == (m.P_o*m.q_out-m.E_cost*m.W_rod)*m.exp(-m.r*m.t)) 204 | m.Equation(m.h.dt() == (1617/2)*(m.q_in - m.q_out)/(m.A_a -m.A_t)) 205 | m.Equation(m.tsi.dt()==1.0) # create time variable 206 | [m.Equation(m.u[i].dt()==m.v[i]) for i in range(npx)] # velocity of rod string 207 | 208 | # Set Objectives ################################################## 209 | m.Obj((m.sa*(1+m.y) + m.sb*(1-m.y))) # objective function to make signum work. 210 | m.Equation((m.sa*(1+m.y) + m.sb*(1-m.y))<= 1e-4) 211 | 212 | 213 | #SetGlobalOptions(Simulation)############################################################## 214 | sim.options.IMODE = 5 # 4 = Dynamic Simulation (Seqential) 215 | sim.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 216 | sim.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 217 | sim.options.time_shift = npt-1 # time shift forward for multiple simulations 218 | sim.options.MAX_ITER = 700 219 | #SetLocalOptions############################################################### 220 | #N/A 221 | 222 | 223 | #MHE########################################################################### 224 | #Parameters (Holds Measured values from MHE) 225 | #hm = mhe.Param(value = 1.0*mhe.TVD.value/2) 226 | #um = mhe.Param(value = sim.u[0]) 227 | fm = mhe.Param(value = sim.f[0]) 228 | 229 | #Variables to estimate and initial conditions 230 | ##FV 231 | #mhe.S.Value = 0 #unitless 232 | #mhe.porosity.Value = .12 233 | #mhe.A_d.Value = 4 234 | 235 | #SetGlobalOptions(MHE)########################################################## 236 | mhe.options.IMODE = 5 # 4 = Dynamic Simulation (Seqential) 237 | mhe.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 238 | mhe.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 239 | mhe.options.time_shift = npt-1 # time shift forward for multiple simulations 240 | mhe.options.MAX_ITER = 700 241 | mhe.Obj((mhe.f[0] -fm)**2) 242 | #SetLocalOptions############################################################### 243 | ##FV 244 | mhe.h.FSTATUS = 0 245 | mhe.h.STATUS = 1 246 | mhe.h.DMAX = 0.05 247 | mhe.h.DCOST = 0.01 248 | #mhe.S.DMAX = 1.0 249 | 250 | #MV 251 | mhe.SPM_in.FSTATUS = 1 252 | mhe.SPM_in.STATUS = 0 253 | 254 | #CV 255 | #mhe.h.FSTATUS = 1 256 | #mhe.h.STATUS = 1 257 | #mhe.h.MEAS_GAP = 0.001 258 | 259 | #Solve######################################################################### 260 | #%% 261 | # Solve the simulation in a loop to simulate a longer horizon 262 | loops = 130 # number of steps forward in time 263 | 264 | #Initialize Storage Values 265 | sim_ts = np.ones(npt)*sim.tsi.value # simulation time storage 266 | sim_hstor = np.ones(npt)*sim.h.value # height of fluid in annulus storage 267 | sim_q_ins= np.ones(npt)*sim.q_in.value # reservoir influx storage 268 | sim_q_outs = np.ones(npt)*sim.q_out.value # production rate storage 269 | sim_P_ress = np.ones(npt)*sim.P_res.value # reservoir pressure storage 270 | sim_Vps = np.ones(npt)*sim.Vp.value # cumulative volume produced storage 271 | sim_NPVs = np.ones(npt)*sim.NPV.value # NPV storage 272 | sim_W_rods = np.ones(npt)*sim.W_rod.value # work of rod (work to lift fluid) storage 273 | sim_SPMs = np.ones(npt)*sim.SPM_in.value # Strokes per minute/ Torque storage Set Points 274 | sim_SPMr = np.ones(npt)*sim.SPM.value #SPM storage 275 | sim_thetas = np.ones(npt)*sim.theta.value#Theta storage 276 | sim_P_wfs = np.ones(npt)*sim.P_wf.value # bottom hole pressure storage 277 | sim_ys = np.ones(npt)*sim.y.value # sign of du/dt storage 278 | 279 | 280 | #MHE Storage 281 | mhe_ts = np.zeros(0) # simulation time storage 282 | mhe_us = [np.array(mhe.u[i].value) for i in range(npx)] # u relative position storage 283 | mhe_vs = [np.array(mhe.v[i].value) for i in range(npx)] 284 | mhe_fs = [np.array(mhe.f[i].value) for i in range(npx)] # dynamic load storage 285 | mhe_hstor = np.zeros(0)# height of fluid in annulus storage 286 | mhe_q_ins= np.zeros(0) # reservoir influx storage 287 | mhe_q_outs = np.zeros(0) # production rate storage 288 | mhe_P_ress = np.zeros(0) # reservoir pressure storage 289 | mhe_Vps = np.zeros(0) # cumulative volume produced storage 290 | mhe_NPVs = np.zeros(0) # NPV storage 291 | mhe_W_rods =np.zeros(0) # work of rod (work to lift fluid) storage 292 | mhe_SPMs = np.zeros(0) # Strokes per minute/ Torque storage Set Points 293 | mhe_SPMr = np.zeros(0) #SPM storage 294 | mhe_thetas = np.zeros(0)#Theta storage 295 | mhe_P_wfs = np.zeros(0) # bottom hole pressure storage 296 | mhe_ys = np.zeros(0) # sign of du/dt storage 297 | mhe_Skins = np.zeros(0) #Skin storage 298 | mhe_porosity = np.zeros(0) 299 | mhe_A_d = np.zeros(0) 300 | 301 | ############################################################### 302 | def push(x, y): 303 | push_len = len(y) 304 | assert len(x) >= push_len 305 | x[:-push_len] = x[push_len:] 306 | x[-push_len:] = y 307 | return x 308 | 309 | for i in range(loops): 310 | 311 | if i>= 20: 312 | sim.SPM_in.VALUE = 13 313 | if i>= 40: 314 | sim.SPM_in.VALUE = 7 315 | if i >=60: 316 | sim.SPM_in.VALUE = 10 317 | 318 | if i >= 80: 319 | sim.SPM_in.VALUE = 8 320 | if i >= 90: 321 | sim.SPM_in.VALUE = 11 322 | if i >= 100: 323 | sim.SPM_in.VALUE = 14 324 | if i >= 105: 325 | sim.SPM_in.VALUE = 7 326 | if i >= 110: 327 | sim.SPM_in.VALUE = 15 328 | if i >= 115: 329 | sim.SPM_in.VALUE = 15 330 | if i >= 120: 331 | sim.SPM_in.VALUE = 6 332 | if i >= 125: 333 | sim.SPM_in.VALUE = 9 334 | 335 | #Solve 336 | sim.solve() 337 | 338 | # measurement noise 339 | f_noise = np.random.normal(0,50,len(sim.f[0].value)) 340 | SPM_noise = np.random.normal(0,.001,len(sim.SPM_in.value)) 341 | 342 | if i == 0: 343 | # Create and store results 344 | sim_ts = np.array(sim.tsi.value) # simulation time storage 345 | sim_us = [np.array(sim.u[i].value) for i in range(npx)] # u relative position storage 346 | sim_vs = [np.array(sim.v[i].value) for i in range(npx)] 347 | sim_fs = [np.array(sim.f[i].value) for i in range(npx)] # dynamic load storage 348 | sim_hstor = np.array(sim.h.value) # height of fluid in annulus storage 349 | sim_q_ins= np.array(sim.q_in.value) # reservoir influx storage 350 | sim_q_outs = np.array(sim.q_out.value) # production rate storage 351 | sim_P_ress = np.array(sim.P_res.value) # reservoir pressure storage 352 | sim_Vps = np.array(sim.Vp.value) # cumulative volume produced storage 353 | sim_NPVs = np.array(sim.NPV.value) # NPV storage 354 | sim_W_rods = np.array(sim.W_rod.value) # work of rod (work to lift fluid) storage 355 | sim_SPMs = np.array(sim.SPM_in.value) # Strokes per minute/ Torque storage Set Points 356 | SPM_meas = np.array(sim.SPM_in.value + SPM_noise) 357 | fm_meas = np.array(sim.f[0].value + f_noise) 358 | sim_SPMr = np.array(sim.SPM.value) #SPM storage 359 | sim_thetas = np.array(sim.theta.value)#Theta storage 360 | sim_P_wfs = np.array(sim.P_wf.value) # bottom hole pressure storage 361 | sim_ys = np.array(sim.y.value) # sign of du/dt storage 362 | f_measured = np.ones(npt*tf_mhe) * sim.f[0].value[0] 363 | spm_measured = np.ones(npt*tf_mhe) * sim.SPM_in.value[0] 364 | 365 | elif i>0: 366 | sim_ts = np.append(sim_ts,sim.tsi.value) # simulation time storage 367 | sim_us = [np.append(sim_us[i],sim.u[i].value) for i in range(npx)] # u relative position storage 368 | sim_vs = [np.append(sim_vs[i],sim.v[i].value) for i in range(npx)] 369 | sim_fs = [np.append(sim_fs[i],sim.f[i].value) for i in range(npx)] # dynamic load storage 370 | sim_hstor = np.append(sim_hstor,sim.h.value) # height of fluid in annulus storage 371 | sim_q_ins= np.append(sim_q_ins,sim.q_in.value) # reservoir influx storage 372 | sim_q_outs = np.append(sim_q_outs,sim.q_out.value) # production rate storage 373 | sim_P_ress = np.append(sim_P_ress,sim.P_res.value) # reservoir pressure storage 374 | sim_Vps = np.append(sim_Vps,sim.Vp.value) # cumulative volume produced storage 375 | sim_NPVs = np.append(sim_NPVs,sim.NPV.value) # NPV storage 376 | sim_W_rods = np.append(sim_W_rods,sim.W_rod.value) # work of rod (work to lift fluid) storage 377 | sim_SPMs = np.append(sim_SPMs,sim.SPM_in.value) # Strokes per minute storage 378 | SPM_meas = np.append(SPM_meas, sim.SPM_in.value + SPM_noise) 379 | fm_meas = np.append(fm_meas, sim.f[0].value + f_noise) 380 | sim_SPMr = np.append(sim_SPMr,sim.SPM.value) #Strokes per minute storage 381 | sim_thetas = np.append(sim_thetas,sim.theta.value) 382 | sim_P_wfs = np.append(sim_P_wfs,sim.P_wf.value) # bottom hole pressure storage 383 | sim_ys = np.append(sim_ys,sim.y.value) # sign of du/dt storage 384 | f_measured = push(f_measured, np.array(sim.f[0].value)) 385 | spm_measured = push(spm_measured, np.array(sim.SPM_in.value)) 386 | #MHE################################### 387 | #Pass height from simulation to MHE 388 | #hm.value = sim.h.value 389 | 390 | 391 | 392 | fm.value = sim.f[0].value + f_noise#f_measured + f_noise #np.flip(f_measured,0) # 393 | #um.value = sim.u[0].value 394 | 395 | #Insert Measurements 396 | mhe.SPM_in.value = sim.SPM_in.value #spm_measured #+ SPM_noise #np.flip(spm_measured, 0) # 397 | 398 | #Solve 399 | mhe.solve() 400 | 401 | #Store new values for plotting 402 | #mhe_Skins = np.append(mhe_Skins, mhe.S.value) 403 | mhe_hstor = np.append(mhe_hstor,mhe.h.value) 404 | #mhe_A_d = np.append(mhe_A_d, mhe.A_d.value) 405 | #mhe_porosity = np.append(mhe_porosity, mhe.porosity.value) 406 | if i == 0: 407 | # Create and store results 408 | mhe_ts = np.array(mhe.tsi.value) # simulation time storage 409 | mhe_us = [np.array(mhe.u[i].value) for i in range(npx)] # u relative position storage 410 | mhe_vs = [np.array(mhe.v[i].value) for i in range(npx)] 411 | mhe_fs = [np.array(mhe.f[i].value) for i in range(npx)] # dynamic load storage 412 | # mhe_hstor = np.array(mhe.h.value) # height of fluid in annulus storage 413 | mhe_q_ins= np.array(mhe.q_in.value) # reservoir influx storage 414 | mhe_q_outs = np.array(mhe.q_out.value) # production rate storage 415 | mhe_P_ress = np.array(mhe.P_res.value) # reservoir pressure storage 416 | mhe_Vps = np.array(mhe.Vp.value) # cumulative volume produced storage 417 | mhe_NPVs = np.array(mhe.NPV.value) # NPV storage 418 | mhe_W_rods = np.array(mhe.W_rod.value) # work of rod (work to lift fluid) storage 419 | mhe_SPMs = np.array(mhe.SPM_in.value) # Strokes per minute/ Torque storage Set Points 420 | # SPM_meas = np.array(sim.SPM_in.value + SPM_noise) 421 | # fm_meas = np.array(sim.f[0].value + f_noise) 422 | mhe_SPMr = np.array(mhe.SPM.value) #SPM storage 423 | mhe_thetas = np.array(mhe.theta.value)#Theta storage 424 | mhe_P_wfs = np.array(mhe.P_wf.value) # bottom hole pressure storage 425 | mhe_ys = np.array(mhe.y.value) # sign of du/dt storage 426 | # f_measured = np.ones(npt*tf_mhe) * sim.f[0].value[0] 427 | # spm_measured = np.ones(npt*tf_mhe) * sim.SPM_in.value[0] 428 | 429 | elif i>0: 430 | mhe_ts = np.append(mhe_ts,mhe.tsi.value) # simulation time storage 431 | mhe_us = [np.append(mhe_us[i],mhe.u[i].value) for i in range(npx)] # u relative position storage 432 | mhe_vs = [np.append(mhe_vs[i],mhe.v[i].value) for i in range(npx)] 433 | mhe_fs = [np.append(mhe_fs[i],mhe.f[i].value) for i in range(npx)] # dynamic load storage 434 | # mhe_hstor = np.append(mhe_hstor,mhe.h.value) # height of fluid in annulus storage 435 | mhe_q_ins= np.append(mhe_q_ins,mhe.q_in.value) # reservoir influx storage 436 | mhe_q_outs = np.append(mhe_q_outs,mhe.q_out.value) # production rate storage 437 | mhe_P_ress = np.append(mhe_P_ress,mhe.P_res.value) # reservoir pressure storage 438 | mhe_Vps = np.append(mhe_Vps,mhe.Vp.value) # cumulative volume produced storage 439 | mhe_NPVs = np.append(mhe_NPVs,mhe.NPV.value) # NPV storage 440 | mhe_W_rods = np.append(mhe_W_rods,mhe.W_rod.value) # work of rod (work to lift fluid) storage 441 | mhe_SPMs = np.append(mhe_SPMs,mhe.SPM_in.value) # Strokes per minute storage 442 | # mhe_meas = np.append(SPM_meas, mhe.SPM_in.value + SPM_noise) 443 | # fm_meas = np.append(fm_meas, sim.f[0].value + f_noise) 444 | mhe_SPMr = np.append(mhe_SPMr,mhe.SPM.value) #Strokes per minute storage 445 | mhe_thetas = np.append(mhe_thetas,mhe.theta.value) 446 | mhe_P_wfs = np.append(mhe_P_wfs,mhe.P_wf.value) # bottom hole pressure storage 447 | mhe_ys = np.append(mhe_ys,mhe.y.value) # sign of du/dt storage 448 | # f_measured = push(f_measured, np.array(sim.f[0].value)) 449 | # spm_measured = push(spm_measured, np.array(sim.SPM_in.value)) 450 | 451 | 452 | 453 | ############################################## 454 | # Plot of Control Parameters and Inputs 455 | plt.clf() 456 | ax=plt.subplot(411) 457 | ax.grid() 458 | plt.plot(sim_ts[0:i*npt],sim_hstor[0:i*npt],'ro',label='Simulation') 459 | plt.plot(sim_ts[0:i*npt],mhe_hstor[0:i*npt],'k-',label= 'MHE') 460 | plt.ylabel('Fluid Height in Annulus') 461 | plt.legend(loc='best') 462 | 463 | ax=plt.subplot(412) 464 | ax.grid() 465 | plt.plot(sim_ts[0:i*npt],sim_SPMs[0:i*npt],'r-',label= 'input') 466 | plt.ylabel('SPM') 467 | plt.xlabel('Time (sec)') 468 | plt.legend(loc='best') 469 | plt.draw() 470 | plt.pause(0.02) 471 | 472 | ax = plt.subplot(413) 473 | ax.grid() 474 | plt.plot(sim_ts[0:i*npt],sim_fs[0][0:i*npt],'b-',label= 'Actual Force') 475 | plt.plot(sim_ts[0:i*npt],fm_meas[0:i*npt],'r--',label= 'Measured Force') 476 | plt.plot(sim_ts[0:i*npt],mhe_fs[0][0:i*npt],'g--',label= 'MHE Force') 477 | 478 | ax = plt.subplot(414) 479 | ax.grid() 480 | plt.plot(sim_ts[0:i*npt],sim_SPMs[0:i*npt],'b-',label= 'Actual torque') 481 | plt.plot(sim_ts[0:i*npt],SPM_meas[0:i*npt],'r--',label= 'Measured torque') 482 | 483 | plt.draw() 484 | plt.pause(0.02) 485 | #Define Dictionary 486 | res = {} 487 | 488 | #Store arrays in dictionary 489 | res['Estimated Annular Fluid Level Height'] = mhe_hstor 490 | res['Actual Annular Fluid Level Height'] = sim_hstor 491 | res['SPM'] = sim_SPMs 492 | res['time'] = sim_ts 493 | res['f_meas'] =fm_meas 494 | res['SPM_meas'] = SPM_meas 495 | 496 | #Save dictionary 497 | np.save('height_estimation_with_noise.npy', res) 498 | #%% 499 | res = np.load('height_estimation_with_noise.npy').item() 500 | 501 | 502 | plt.clf() 503 | ax=plt.subplot(211) 504 | #Set Global axis option 505 | plt.rcParams['xtick.labelsize'] = 12 506 | plt.rcParams['ytick.labelsize'] = 12 507 | 508 | ax.grid() 509 | plt.plot(res['time'][0:i*npt],res['Actual Annular Fluid Level Height'][0:i*npt],'ro',label='True') 510 | plt.plot(res['time'][0:i*npt],res['Estimated Annular Fluid Level Height'][0:i*npt],'k-',label= 'Estimated') 511 | plt.ylabel('Fluid Height in Annulus (ft)', fontsize = 12) 512 | plt.tick_params(labelbottom = False) 513 | plt.legend(loc='best', fontsize = 12) 514 | 515 | ax=plt.subplot(212) 516 | ax.grid() 517 | plt.plot(res['time'][0:i*npt],res['SPM'][0:i*npt],'b-',label= '$T_{net}$') 518 | plt.plot(res['time'][0:i*npt],res['SPM_meas'][0:i*npt], 'r--',r'$T_{net_{meas}}$' ) 519 | plt.ylabel('$T_{net}$ (ft-lbs)', fontsize = 12) 520 | plt.xlabel('Time (seconds)', fontsize = 12) 521 | plt.legend(loc='best', fontsize = 12) 522 | plt.draw() 523 | 524 | #plt.savefig(r'C:\Users\Brandon\Documents\SCHOOL\OPENCOURSEWARE\BYU\DYNAMIC_OPTIMIZATION\ASSIGNMENTS\CLASS_SPRING_2018\PROJECT\RENDERED_FIGURES\MHERender_Height_050318.eps', transparent=True, dpi = 1200) 525 | 526 | #%% 527 | 528 | 529 | -------------------------------------------------------------------------------- /MHE_PI_reduced.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Apr 6 13:20:42 2018 4 | 5 | @author: Brandon 6 | """ 7 | 8 | #from __future__ import division # compatibility with python 2.7 9 | from gekko import GEKKO 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | import time 13 | 14 | #%% 15 | #Timespace for MPC and MHE########################### 16 | tf =1.0 # sec, length of simulation 17 | npt = 11 # number of time discretizations 18 | nit = 1 # number of itterations to solve 19 | ti = np.linspace(0,tf,npt) # times for plotting 20 | 21 | #Simulation Timespace############################### (Added) 22 | npt_sim = 21 23 | tf_sim =1.0 24 | 25 | # Define time space for MPC 26 | tf_mpc = 1.0 27 | npt_mpc = npt*tf_mpc 28 | 29 | 30 | # Define Rod Discretizations 31 | TVD_d = 4800 # ft, lenth of rod 32 | npx =10 # number of rod discretizations 33 | npx_m = 5 34 | 35 | 36 | 37 | dx = TVD_d/(npx-1) # #ft lenth of rod discretizations 38 | dx_m = TVD_d/(npx_m-1) 39 | xi = np.linspace(0,TVD_d,npx) # possitions allong rod (for plotting) 40 | xi_m= np.linspace(0,TVD_d,npx_m) 41 | #Set Points 42 | SPM_in = np.ones(npt)*10 43 | 44 | 45 | #BuildModel#################################################################### 46 | mpc = GEKKO() 47 | sim = GEKKO() 48 | mhe = GEKKO() 49 | 50 | 51 | #Horizon Window 52 | mhe.time = np.linspace(0,tf,npt) 53 | time_mpc = np.linspace(0,tf,npt) 54 | time_mpc = np.append(time_mpc, [1.1,1.4,2,2.5,3,3.5,4,4.5,5,5.5,6.5,7]) 55 | mpc.time = time_mpc#np.linspace(0,tf_mpc,npt_mpc) 56 | sim.time = np.linspace(0,tf_sim,npt_sim) 57 | 58 | 59 | ################################ 60 | # Conventional Rod Pump Unit Geometry 61 | # API geometry dimension values 62 | Ag=210.0 63 | Cg=120.3 64 | Ig=120.0 65 | Pg=148.5 66 | Hg=237.88 67 | Gg=86.88 68 | Rg=47.0 69 | 70 | #lengths from FIG. 1 - Beam Pumping Unit Shown as a Four-Bar Linkage 71 | L_1 = Rg 72 | L_2 = np.sqrt((Hg-Gg)**2.0+Ig**2.0) 73 | L_3 = Cg 74 | L_4 = Pg 75 | L_5 = Ag 76 | 77 | starting_height = 3/4 78 | 79 | #Setpoints 80 | level_height = 3 81 | SP = starting_height*TVD_d-level_height 82 | dSP = 0.1 83 | 84 | 85 | 86 | #Simulation######################################## 87 | 88 | for m in [sim,mpc,mhe]: 89 | #Constants 90 | m.API = m.Const(value = 45) #API gravity of fluid, unitless 91 | m.c = m.Const(value = 0.000013) #Compressibility, psi^-1 92 | m.k = m.Const(value = 15) #Permeability, md 93 | m.Bo = m.Const(value = 1.2) #FVF, rb/STB 94 | m.A_d = m.FV(value = 2, ub = 8, lb = 1) #Drainage Area, Acres 95 | m.sw = m.Const(value = 0.2) #Water Saturation 96 | m.porosity = m.FV(value = 0.08, ub = .12, lb = 0.07) #Porosity, unitless 97 | m.gamma_E = m.Const(value = 1.78) #Euler Constant 98 | m.C_a = m.Const(value = 31.6) #Drainage Area Shape Factor (Circular) 99 | m.rw = m.Const(value = 0.328) #Welbore radius, ft 100 | m.S = m.FV(value = 0, ub = 10, lb = -5) #unitless 101 | m.u_visc = m.Const(value = 1.5) # Viscosity, cp 102 | m.h_pz = m.Const(value = 8) #pay zone thickness, ft 103 | m.D_t = m.Const(value = 2.5) # tubing diameter, in 104 | m.St_length = m.Const(value = 85) # rod pump stroke length, in 105 | 106 | m.g = m.Const(value = 32.2) # acceleration due to gravity, ft/s^3 107 | m.g_conv= m.Const(value = 32.2) # lbf conversion , lb-ft/s^2-lbf 108 | m.rho_r = m.Const(value = 490) # lbs/ft^3, density of rod steel 109 | m.rho_w = m.Const(value = 62.3 ) # lbs/ft^3, density of water at standard conditions 110 | m.a = m.Const(value =18996.06 ) # ft/s speed of sound in steel 111 | m.D_r = m.Const(value = 1.0) # in, diameter of rod string 112 | m.Ac = m.Const(value= m.D_r.value**2/4.0*np.pi) # in^2, cross sectional area of rod 113 | m.nu = m.Const(value = 0.01) # unitless, damping coefficient 114 | m.pi = m.Const(value=np.pi) 115 | m.E = m.Const(value = 32025000.0) # psi sucker rod modulus of elasticity 116 | m.alpha = m.Const(value = 0.0) # pump parameter, unitless 117 | m.beta = m.Const(value = 1.0) # pump parameter, unitless 118 | 119 | m.L_1 = m.Const(value =L_1) # unit geometry 120 | m.L_2 = m.Const(value =L_2) # unit geometry 121 | m.L_3 = m.Const(value =L_3) # unit geometry 122 | m.L_4 = m.Const(value =L_4) # unit geometry 123 | m.L_5 = m.Const(value =L_5) # unit geometry 124 | 125 | if m == sim: 126 | m.dx = m.Const(value = dx) # ft delta x 127 | else: 128 | m.dx = m.Const(value = dx_m) 129 | 130 | #Prime Mover Constants (Torque Balance) 131 | m.tau_p = m.Const(value = 3) #tau 132 | m.k_gain = m.Const(value = 1) #one to one ratio between torque and SPM 133 | 134 | ##Economic 135 | m.Weight_lb_ft = m.Const(value = m.rho_r.value*m.Ac.value*m.g.value/m.g_conv/144) #Weight of rod string, lbf/ft 136 | m.Capex = m.Const(value = 200000) #Cost of Pumping Rod Unit,$? 137 | m.P_o = m.Const(value = 50) #Price of Oil, $/STB 138 | m.r = m.Const(value= .12/365) #Daily Discount Rate, % 139 | m.P_th = m.Const(value = 100) #tubing head pressure, psi 140 | m.TVD = m.Const(value = 4800) #true vertical depth, ft 141 | m.E_cost = m.Const(value = 0.13/3600) #Cost of Electricity, cents/Kws 142 | 143 | #Calculated Constants #DO NOT MODIFY# 144 | m.Wr = m.Const(value = m.TVD.value*m.Weight_lb_ft.value) #Weight of entire rod string, lbm 145 | m.D_a = m.Const(value = 2*12*m.rw.value) #Annulus Diameter, in 146 | m.gamma = m.Const(141.5/(m.API.value+131.5)) #Specific gravity of Fluid 147 | m.P_startpump = m.Const(value = 0.433*m.gamma.value*m.TVD.value) #Average Reservoir Pressure at Pump start up 148 | m.Pi = m.Const(value = .433*m.TVD.value) #Initial Reservoir Pressure, psi 149 | 150 | m.A_t = m.Const((np.pi/4)*m.D_t.value**2) #Cross sectional Area of tubing, in^2 151 | m.A_a = m.Const((np.pi/4)*m.D_a.value**2) #Cross Sectional Area of Annulus, in^2 152 | m.Wf = m.Const(value = m.TVD.value*m.rho_w.value*m.gamma.value*m.g.value/m.g_conv.value*(m.A_t.value-m.Ac.value)/144) # lbf, weight of fluid in tubing 153 | 154 | #MV's 155 | m.SPM_in = m.MV(value = 15, lb = 5, ub = 15) #Rod Pump Pumping Speed/Torque, spm 156 | 157 | #Variables 158 | m.V_i= m.Var(value = 7758*m.A_d.value*m.h_pz.value*m.porosity.value*(1-m.sw.value)/m.Bo.value) #OOIP, stb 159 | m.Vp = m.Var(value = m.V_i.value*(np.exp(m.c.value*(m.Pi.value-m.P_startpump.value))-1)) #initial volume produced prior stb 160 | 161 | if m == sim or m == mpc: 162 | m.h = m.CV(value = 1.0*m.TVD.value*starting_height) 163 | else: 164 | m.h_mv = m.MV(value = 1.0*m.TVD.value*starting_height, lb = 0, ub = 4800) 165 | m.h = m.Var(value = 1.0*m.TVD.value*starting_height, lb = 0, ub = 4800) # Height, ft 166 | m.Equation(m.h_mv == m.h) 167 | m.NPV = m.Var(value = -1.0*m.Capex.value) #Net Present Value, $ 168 | m.y = m.Var( lb = -1, ub = 1) # SIGN(x) 169 | m.sa = m.Var(value = 0, lb = 0) # slack variable a 170 | m.sb = m.Var(value = 0, lb = 0) # slack variable b 171 | m.tsi = m.Var(value = 0.0) # mulation time 172 | m.SPM = m.Var(value = 15) #SPM, strokes/min 173 | #omega = m.Var(value = 0) 174 | m.theta = m.Var(value = 0) # rad i.e sec^-1 crank angle of surface unit 175 | if m == sim: 176 | m.u = [m.SV(value = 9.22) for i in range(npx)] # relative position of each rod segment 177 | m.v = [m.Var(value = 0.0) for i in range(npx)] # velocity of reach rod segment 178 | m.f = [m.SV(value = 0.0) for i in range (npx)] # load at each rod segment 179 | 180 | else: 181 | m.u = [m.SV(value = 9.22) for i in range(npx_m)] # relative position of each rod segment 182 | m.v = [m.Var(value = 0.0) for i in range(npx_m)] # velocity of reach rod segment 183 | m.f = [m.SV(value = 0.0) for i in range (npx_m)] # load at each rod segment 184 | 185 | m.P = m.Var(value = 1e-6) # unitless, load at the pump 186 | 187 | 188 | ## State Variables 189 | m.P_res = m.Var(value = m.P_startpump.value*1.0) #Current Reservoir Pressure , psi 190 | m.P_wf = m.Var(value = 0.433*m.gamma*m.h.value) #Bottomhole Flowing Pressure, psi 191 | m.q_in = m.Var(value = (1/86400)*m.k.value*m.h_pz.value*(m.P_res.value-m.P_wf.value)/(141.2*m.Bo.value*m.u_visc.value*((1/2)*np.log(4*m.A_d.value/(m.gamma_E.value*m.C_a.value*m.rw.value**2)) + m.S.value))) #IPR-VLP Flow rate, STB/s 192 | m.q_out = m.Var(value = 0) # Outgoing Flow Rate, STB/s 193 | m.t = m.Var(value = 0) #Time, days 194 | m.W_rod = m.Var(value = (1.0962)*m.q_out.value*(m.P_th.value-m.P_wf.value + .433*m.gamma.value*m.TVD.value) + (4.7053e-7)*m.Wr.value*m.St_length.value*m.SPM.value) #Work supplied by electric Motor, KW 195 | 196 | #Intermediates 197 | m.hs = m.Intermediate(m.sqrt(L_1**2 +L_2**2 + 2 *L_1 *L_2 *m.cos(m.theta))) 198 | 199 | #Equations 200 | ##AlgebraicEqns 201 | m.Equation(m.V_i == 7758*m.A_d*m.h_pz*m.porosity*(1-m.sw)/m.Bo) 202 | m.Equation(m.P_wf == 0.433*m.gamma*m.h) 203 | m.Equation(m.P_res == m.Pi-(1/m.c)*m.log((m.Vp/m.V_i)+1)) 204 | m.Equation(m.q_in == (1/86400)*m.k*m.h_pz*(m.P_res-m.P_wf)/(141.2*m.Bo*m.u_visc*((1/2)*m.log(4*m.A_d/(m.gamma_E*m.C_a*m.rw**2)) + m.S))) #STB/s 205 | m.Equation(m.W_rod == (1.0962)*m.q_out*(m.P_th-m.P_wf + .433*m.gamma*m.TVD) + (4.7053e-7)*m.Wr*m.St_length*m.SPM) 206 | 207 | #Prime Mover Equations- Torque Balance and Kinematic Eqns 208 | m.Equation(m.SPM.dt() == -(1/m.tau_p)*m.SPM + (m.k_gain/m.tau_p)*m.SPM_in) 209 | m.Equation((2*m.pi/60)*m.SPM == m.theta.dt()) 210 | 211 | m.Equation(m.u[0] == (1/12)*L_5*(m.asin(L_1*m.sin(m.theta)/m.hs)+m.acos((m.hs**2+L_3**2-L_4**2)/(2*L_3*m.hs)))) # position of polished rod, inches 212 | 213 | if m == sim: 214 | [m.Equation(m.v[i+1].dt()== m.a**2 * (m.u[i+2] - 2.0*m.u[i+1] + m.u[i])/m.dx**2 - m.pi*m.a*m.nu/(2.0*m.TVD)*m.v[i+1] - (1-m.rho_w*m.gamma/m.rho_r)*m.g) for i in range(npx-2) ]# wave equation 215 | else: 216 | [m.Equation(m.v[i+1].dt()== m.a**2 * (m.u[i+2] - 2.0*m.u[i+1] + m.u[i])/m.dx**2 - m.pi*m.a*m.nu/(2.0*m.TVD)*m.v[i+1] - (1-m.rho_w*m.gamma/m.rho_r)*m.g) for i in range(npx_m-2) ]# wave equation 217 | 218 | m.Equation(m.q_out == m.A_t * m.u[-1].dt()*12/231/42 * (1+m.y)/2) # rate of fluid production, barrels/ 219 | 220 | # Equations for calculating rod loading 221 | # Load at surface 222 | m.Equation(m.f[0] == m.E*m.Ac*1/2/m.dx *(-m.u[2] + 4*m.u[1] -3*m.u[0])) 223 | # Load at pump 224 | 225 | if m == sim: 226 | m.Equation(m.f[npx-1] == m.E*m.Ac* m.P) 227 | # load at intermediate points 228 | [m.Equation(m.f[1+i] == m.E*m.Ac*1/2.0/dx*(m.u[i+2] - m.u[i])) for i in range(npx-2)] 229 | # pump boundary 230 | m.Equation( m.u[npx-1]*m.alpha + (m.u[npx-1] - m.u[npx-2])/dx == m.P) 231 | #add in signum for lifting and lowering conditions 232 | else: 233 | m.Equation(m.f[npx_m-1] == m.E*m.Ac* m.P) 234 | # load at intermediate points 235 | [m.Equation(m.f[1+i] == m.E*m.Ac*1/2.0/dx*(m.u[i+2] - m.u[i])) for i in range(npx_m-2)] 236 | # pump boundary 237 | m.Equation( m.u[npx_m-1]*m.alpha + (m.u[npx_m-1] - m.u[npx_m-2])/dx == m.P) 238 | #add in signum for lifting and lowering conditions 239 | m.Equation(m.v[-1] == m.sb - m.sa ) 240 | m.Equation(m.P == -((m.Wf- (m.A_t - m.Ac)*m.P_wf)/m.E/m.Ac) * (1 + m.y)/2 ) # -P_wf*A_t 241 | 242 | 243 | ##DifferentialEans 244 | m.Equation(m.t.dt() == 1) 245 | m.Equation(m.Vp.dt() == m.q_in) 246 | m.Equation(m.NPV.dt() == (m.P_o*m.q_out-m.E_cost*m.W_rod)*m.exp(-m.r*m.t)) 247 | m.Equation(m.h.dt() == (1617/2)*(m.q_in - m.q_out)/(m.A_a -m.A_t)) 248 | m.Equation(m.tsi.dt()==1.0) # create time variable 249 | 250 | if m == sim: 251 | [m.Equation(m.u[i].dt()==m.v[i]) for i in range(npx)] # velocity of rod string 252 | else: 253 | [m.Equation(m.u[i].dt()==m.v[i]) for i in range(npx_m)] # velocity of rod string 254 | 255 | # Set Objectives ################################################## 256 | if m == sim: 257 | m.Obj((m.sa*(1+m.y) + m.sb*(1-m.y))) # objective function to make signum work. 258 | elif m== mhe: 259 | m.Obj((m.sa*(1+m.y) + m.sb*(1-m.y))) # objective function to make signum work. 260 | m.Equation((m.sa*(1+m.y) + m.sb*(1-m.y))<=1e-4) 261 | else: 262 | m.Obj((m.sa*(1+m.y) + m.sb*(1-m.y))) # objective function to make signum work. 263 | #m.Equation((m.sa*(1+m.y) + m.sb*(1-m.y))<=1e-3) 264 | #SetGlobalOptions(Simulation)############################################################## 265 | sim.options.IMODE = 5 # 4 = Dynamic Simulation (Seqential) 266 | sim.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 267 | sim.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 268 | sim.options.time_shift = npt_sim-1 # time shift forward for multiple simulations 269 | sim.options.MAX_ITER = 450 270 | 271 | #SetLocalOptions############################################################### 272 | #N/A 273 | sim.SPM_in.FSTATUS = 1 # accept measurments 274 | sim.SPM_in.STATUS = 0 # don't let optimizer change (simulation) 275 | 276 | #MHE########################################################################### 277 | #Parameters (Holds Measured values from MHE) 278 | fm = mhe.Param(value = sim.f[0].value) 279 | 280 | #SetGlobalOptions(MHE)########################################################## 281 | mhe.options.IMODE = 5 # 4 = Dynamic Simulation (Seqential) 282 | mhe.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 283 | mhe.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 284 | mhe.options.time_shift = npt-1 # time shift forward for multiple simulations 285 | mhe.options.MAX_ITER = 1000 286 | mhe.Obj((mhe.f[0] - fm)**2) 287 | 288 | #SetLocalOptions (MHE)############################################################### 289 | ##FV #Variable to estimate 290 | mhe.h_mv.FSTATUS = 0 291 | mhe.h_mv.STATUS = 1 292 | #mhe.h.DMAX = 0.5 293 | mhe.h_mv.DMAX = 0.5#0.05 294 | mhe.h_mv.DCOST = 0.01 295 | 296 | #MV 297 | mhe.SPM_in.FSTATUS = 1 298 | mhe.SPM_in.STATUS = 0 299 | 300 | #SetGlobalOptions(MPC)########################################################## 301 | mpc.options.IMODE = 6 # 4 = Dynamic Simulation (Seqential) 302 | mpc.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 303 | mpc.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 304 | mpc.options.time_shift = npt-1 # time shift forward for multiple simulations 305 | mpc.options.MAX_ITER = 450 306 | mpc.options.CV_TYPE = 1 307 | 308 | #SetLocalOptions############################################################### 309 | ##FV 310 | mpc.SPM_in.STATUS = 1 311 | mpc.SPM_in.FSTATUS = 0 # cound be meausured, but we are setting it 312 | mpc.SPM_in.DMAX = .05 # maximum change in SPM per cycle 313 | #mpc.SPM_in.DCOST = .01 314 | 315 | # CV's 316 | mpc.h.STATUS = 1 # control h 317 | mpc.h.FSTATUS = 1 # accept measurements 318 | mpc.h.TR_INIT = 2 # create trajectory to setpoint 319 | mpc.h.TAU = .1 # time constant of trajectory to h set point 320 | 321 | #Solve######################################################################### 322 | #%% 323 | # Solve the simulation in a loop to simulate a longer horizon 324 | loops = 180 # number of steps forward in time 325 | res = {} 326 | solve_stat = np.zeros(loops) 327 | t_cycle = 0.0 328 | 329 | #PID Options ###################################################### 330 | e = np.zeros(loops) 331 | ie = np.zeros(loops) 332 | op = np.zeros(loops) 333 | 334 | #SPM_output 335 | op[0] = np.ones(1)*sim.SPM_in.value 336 | ophi = 15 337 | oplo = 5 338 | 339 | pv = np.zeros(loops) 340 | P = np.zeros(loops) 341 | I = np.zeros(loops) 342 | pv_ave = np.zeros(loops) 343 | 344 | #Tuning 345 | kc = 1 346 | tauI = 1 347 | #Initialize Storage Values 348 | sim_ts = np.ones(npt_sim)*sim.tsi.value # simulation time storage 349 | sim_hstor = np.ones(npt_sim)*sim.h.value # height of fluid in annulus storage 350 | sim_q_ins= np.ones(npt_sim)*sim.q_in.value # reservoir influx storage 351 | sim_q_outs = np.ones(npt_sim)*sim.q_out.value # production rate storage 352 | sim_P_ress = np.ones(npt_sim)*sim.P_res.value # reservoir pressure storage 353 | sim_Vps = np.ones(npt_sim)*sim.Vp.value # cumulative volume produced storage 354 | sim_NPVs = np.ones(npt_sim)*sim.NPV.value # NPV storage 355 | sim_W_rods = np.ones(npt_sim)*sim.W_rod.value # work of rod (work to lift fluid) storage 356 | sim_SPMs = np.ones(npt_sim)*sim.SPM_in.value # Strokes per minute/ Torque storage Set Points 357 | sim_SPMr = np.ones(npt_sim)*sim.SPM.value #SPM storage 358 | sim_thetas = np.ones(npt_sim)*sim.theta.value#Theta storage 359 | sim_P_wfs = np.ones(npt_sim)*sim.P_wf.value # bottom hole pressure storage 360 | sim_ys = np.ones(npt_sim)*sim.y.value # sign of du/dt storage 361 | 362 | 363 | #MHE Storage 364 | mpc_ts = np.zeros(0) # simulation time storage 365 | mhe_us = [np.array(mpc.u[i].value) for i in range(npx_m)] # u relative position storage 366 | mhe_vs = [np.array(mpc.v[i].value) for i in range(npx_m)] 367 | mhe_fs = [np.array(mpc.f[i].value) for i in range(npx_m)] # dynamic load storage 368 | mpc_hstor = np.zeros(0)# height of fluid in annulus storage 369 | mpc_q_ins= np.zeros(0) # reservoir influx storage 370 | mpc_q_outs = np.zeros(0) # production rate storage 371 | mpc_P_ress = np.zeros(0) # reservoir pressure storage 372 | mpc_Vps = np.zeros(0) # cumulative volume produced storage 373 | mpc_NPVs = np.zeros(0) # NPV storage 374 | mpc_W_rods =np.zeros(0) # work of rod (work to lift fluid) storage 375 | mpc_SPMs = np.zeros(0) # Strokes per minute/ Torque storage Set Points 376 | mpc_SPMr = np.zeros(0) #SPM storage 377 | mpc_thetas = np.zeros(0)#Theta storage 378 | mpc_P_wfs = np.zeros(0) # bottom hole pressure storage 379 | mpc_ys = np.zeros(0) # sign of du/dt storage 380 | mpc_Skins = np.zeros(0) #Skin storage 381 | mpc_porosity = np.zeros(0) 382 | mpc_A_d = np.zeros(0) 383 | mhe_Skins = np.zeros(0) #Skin storage 384 | mhe_hstor = np.zeros(0)# height of fluid in annulus storage 385 | mpc_Skins = np.zeros(0) 386 | mpc_Skinss = np.zeros(0) 387 | mpc_h = np.zeros(0) 388 | mpc_hss = np.zeros(0) 389 | ############################################################### 390 | 391 | 392 | for i in range(loops): 393 | 394 | # simulate system for 1 second 395 | sim.solve() # (remote = False) for local 396 | if i == 0: 397 | # Create and store results 398 | sim_ts = np.array(sim.tsi.value) # simulation time storage 399 | sim_us = [np.array(sim.u[i].value) for i in range(npx)] # u relative position storage 400 | sim_vs = [np.array(sim.v[i].value) for i in range(npx)] 401 | sim_fs = [np.array(sim.f[i].value) for i in range(npx)] # dynamic load storage 402 | sim_hstor = np.array(sim.h.value) # height of fluid in annulus storage 403 | sim_q_ins= np.array(sim.q_in.value) # reservoir influx storage 404 | sim_q_outs = np.array(sim.q_out.value) # production rate storage 405 | sim_P_ress = np.array(sim.P_res.value) # reservoir pressure storage 406 | sim_Vps = np.array(sim.Vp.value) # cumulative volume produced storage 407 | sim_NPVs = np.array(sim.NPV.value) # NPV storage 408 | sim_W_rods = np.array(sim.W_rod.value) # work of rod (work to lift fluid) storage 409 | sim_SPMs = np.array(sim.SPM_in.value) # Strokes per minute/ Torque storage Set Points 410 | sim_SPMr = np.array(sim.SPM.value) #SPM storage 411 | sim_thetas = np.array(sim.theta.value)#Theta storage 412 | sim_P_wfs = np.array(sim.P_wf.value) # bottom hole pressure storage 413 | sim_ys = np.array(sim.y.value) # sign of du/dt storage 414 | 415 | elif i>0: 416 | sim_ts = np.append(sim_ts,sim.tsi.value) # simulation time storage 417 | sim_us = [np.append(sim_us[i],sim.u[i].value) for i in range(npx)] # u relative position storage 418 | sim_vs = [np.append(sim_vs[i],sim.v[i].value) for i in range(npx)] 419 | sim_fs = [np.append(sim_fs[i],sim.f[i].value) for i in range(npx)] # dynamic load storage 420 | sim_hstor = np.append(sim_hstor,sim.h.value) # height of fluid in annulus storage 421 | sim_q_ins= np.append(sim_q_ins,sim.q_in.value) # reservoir influx storage 422 | sim_q_outs = np.append(sim_q_outs,sim.q_out.value) # production rate storage 423 | sim_P_ress = np.append(sim_P_ress,sim.P_res.value) # reservoir pressure storage 424 | sim_Vps = np.append(sim_Vps,sim.Vp.value) # cumulative volume produced storage 425 | sim_NPVs = np.append(sim_NPVs,sim.NPV.value) # NPV storage 426 | sim_W_rods = np.append(sim_W_rods,sim.W_rod.value) # work of rod (work to lift fluid) storage 427 | sim_SPMs = np.append(sim_SPMs,sim.SPM_in.value) # Strokes per minute storage 428 | sim_SPMr = np.append(sim_SPMr,sim.SPM.value) #Strokes per minute storage 429 | sim_thetas = np.append(sim_thetas,sim.theta.value) 430 | sim_P_wfs = np.append(sim_P_wfs,sim.P_wf.value) # bottom hole pressure storage 431 | sim_ys = np.append(sim_ys,sim.y.value) # sign of du/dt storage 432 | solve_stat[i] = t_cycle 433 | ##MHE################################################################## 434 | #Insert Measurements to MHE 435 | fm.value = np.array(sim.f[0].value)[0:npt_sim:2] #(Modified) 436 | 437 | mhe.SPM_in.value = np.array(sim.SPM_in.value)[0:npt_sim:2] #(Modified) 438 | 439 | #Solve 440 | t_start = time.time() 441 | mhe.solve() # (remote = False) for local solve 442 | 443 | #Pass values to MPC 444 | pv[i] = mhe.h_mv.NEWVAL 445 | pv_ave[i] = np.average(pv[i-10:i]) 446 | #Store new values for plotting 447 | mhe_hstor = np.append(mhe_hstor,mhe.h.value) 448 | 449 | # ##PID ##################################################################### 450 | # if i<10: 451 | e[i] = pv[i] - SP 452 | # if i>10: 453 | # e[i] = pv_ave[i] - SP 454 | # 455 | # if i >= 1: 456 | ie[i] = ie[i-1] + e[i]*tf 457 | 458 | P[i] = kc * e[i] 459 | I[i] = kc/tauI * ie[i] 460 | 461 | op[i] = op[0] + P[i] + I[i] 462 | 463 | #Anti-reset Wind up 464 | if op[i] > 15: 465 | op[i] = ophi 466 | ie[i] = ie[i] - e[i]*tf 467 | if op[i] < 5: 468 | op[i] = oplo 469 | ie[i] = ie[i] - e[i]*tf 470 | 471 | #Pass output to simulation 472 | t_end = time.time() 473 | t_cycle = t_end - t_start 474 | sim.SPM_in.value = np.ones(npt_sim)*op[i] 475 | 476 | ####################################################################### 477 | 478 | # Plotting 479 | plt.clf() 480 | ax=plt.subplot(311) 481 | ax.grid() 482 | plt.plot(sim_ts[0:i*npt_sim],sim_SPMs[0:i*npt_sim],'ro',label='SPM Set Point') 483 | plt.plot(sim_ts[0:i*npt_sim],sim_SPMr[0:i*npt_sim],'bo',label='SPM') 484 | plt.ylabel('Strokes per Minute') 485 | plt.legend(loc=2) 486 | ax=plt.subplot(312) 487 | ax.grid() 488 | plt.plot(sim_ts[0:i*npt_sim],sim_hstor[0:i*npt_sim],'k-',label= 'Height') 489 | plt.plot(sim_ts[0:i*npt_sim], np.ones(i*npt_sim)*SP, label = 'height SP') 490 | #plt.plot(ts[0:i*npt], mpc_hs[0:npt*i], label = 'mpc height') 491 | plt.ylabel('Annular Fluid Height (ft)') 492 | plt.legend(loc='best') 493 | ax = plt.subplot(313) 494 | ax.grid() 495 | plt.plot(sim_ts[0:i*npt_sim], sim_q_outs[0:i*npt_sim], label = 'q_out') 496 | plt.plot(sim_ts[0:i*npt_sim], sim_q_ins[0:i*npt_sim], label = 'q_in') 497 | plt.legend() 498 | plt.ylabel('Flow Rate, STB/s') 499 | plt.xlabel('Time (sec)') 500 | plt.draw() 501 | plt.pause(0.02) 502 | 503 | #%% 504 | 505 | res['solve_stat'] = solve_stat 506 | res['ts' ] = sim_ts 507 | res['us' ] = sim_us 508 | res['vs' ] = sim_vs 509 | res['fs'] = sim_fs 510 | res['hstor' ] = sim_hstor 511 | res[ 'q_ins' ] = sim_q_ins 512 | res[ 'q_outs' ] = sim_q_outs 513 | res['P_ress' ] = sim_P_ress 514 | res['Vps' ] = sim_Vps 515 | res[ 'NPVs' ] = sim_NPVs 516 | res['W_rods' ] = sim_W_rods 517 | res['SPMs'] = sim_SPMs 518 | res['SPMr'] = sim_SPMr 519 | res['thetas'] = sim_thetas 520 | res['P_wfs'] = sim_P_wfs 521 | res[ 'ys'] = sim_ys 522 | res['h_SP'] = np.ones(loops*npt)*SP 523 | 524 | #%% 525 | np.save('PI_MHE_Control_Results_10npt_5npt.npy', res) 526 | #%% 527 | # Load dictionary of results 528 | res = np.load('PI_MHE_Control_Results_10npt_5npt.npy').item() 529 | 530 | #%% Plotting from dictionary 531 | 532 | 533 | plt.figure() 534 | plt.rcParams['xtick.labelsize'] = 12 535 | plt.rcParams['ytick.labelsize'] = 12 536 | ax=plt.subplot(311) 537 | ax.grid() 538 | plt.plot(res['ts'], res['SPMs'], 'r--', label=r'$T_{net}$ (ft-lb)')#, s = 4, c='b' ) # 'ro', for latex 539 | plt.plot(res['ts'], res['SPMr'],'b-', label=r'Actual')#, s = 4, c = 'r') #'bo', 540 | plt.ylabel('SPM', fontsize = 12) 541 | plt.legend(loc= 1,fontsize = 12) 542 | plt.xlim(0,180) 543 | ax=plt.subplot(312) 544 | ax.grid() 545 | plt.plot(res['ts'], res['hstor'],'k-',label= 'Actual') 546 | plt.plot(res['ts'], np.ones(np.size(res['ts']))*(sim.TVD.value*3/4 -3), label = 'SP') # fix 547 | #plt.plot(ts[0:i*npt], mpc_hs[0:npt*i], label = 'mpc height') 548 | plt.ylabel('Fluid Level (ft)', fontsize = 12) 549 | plt.legend(loc=1,fontsize = 12) 550 | plt.xlim(0,180) 551 | ax = plt.subplot(313) 552 | ax.grid() 553 | plt.plot(res['ts'], res['q_outs'], label = r'$q_{out}$') 554 | plt.plot(res['ts'], res['q_ins'], label = r'$q_{in}$') 555 | plt.legend(loc = 1,fontsize = 12) 556 | plt.ylabel('Flow (STB/s)', fontsize = 12) 557 | plt.xlabel('Time (seconds)', fontsize = 12) 558 | plt.xlim(0,180) 559 | plt.draw() 560 | 561 | #plt.legend(fontsize = 12) 562 | #plt.ylabel('Fluid Level (ft)', fontsize = 12) 563 | #plt.xlabel('Time (seconds)', fontsize = 12) 564 | 565 | plt.tight_layout() 566 | plt.savefig('PI_MHE_Control_K_10_Tau_5.eps', transparent = True, dpi = 1200) 567 | plt.show() 568 | #%% 569 | # timing figure 570 | plt.rcParams['xtick.labelsize'] = 12 571 | plt.rcParams['ytick.labelsize'] = 12 572 | plt.figure() 573 | plt.plot(np.linspace(1,180,179), res['solve_stat'][1:],'r-',label='Solve Time') 574 | plt.plot(np.linspace(1,loops,loops-1), np.ones(loops-1)*np.average(res['solve_stat'][1:]),'b--', label = 'Average') 575 | plt.plot(np.linspace(1,loops,loops-1), np.ones(loops-1),'k:', label = 'Real Time') 576 | plt.xlabel('Control Cycle', fontsize = 12) 577 | plt.ylabel('Computation Time (seconds)', fontsize = 12) 578 | plt.legend(fontsize = 12) 579 | plt.ylim(0,4) 580 | plt.xlim(0,180) 581 | plt.savefig('PI_MHE_Simulation_Timing.eps', dpi = 1200, transparent = True) 582 | plt.savefig('PI_MHE_Simulation_Timing.png', dpi = 200, transparent = True) 583 | plt.show() 584 | 585 | 586 | print('Average Solve Time '+str(np.average(res['solve_stat'][1:]))+'(s)') 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | #%% 600 | ## Figure 1 ## Height in the Annulus and incoming and outgoing flow rate#### 601 | plt.figure(1, figsize = (6,4.5)) 602 | plt.subplot(211) 603 | plt.plot(sim_ts, sim_hstor, 'r--', label = 'height in annulus') 604 | plt.plot(sim_ts, np.ones(len(sim_ts))*SP, 'b--', label = 'height Set Point') 605 | plt.ylabel('height, ft') 606 | plt.legend() 607 | 608 | plt.subplot(212) 609 | plt.plot(sim_ts, sim_q_ins, 'b--', label = r'$q_{in}$') 610 | plt.plot(sim_ts, sim_q_outs, 'g--', label = r'$q_{out}$') 611 | plt.ylabel('Flow Rate, STB/s') 612 | plt.xlabel('time, sec') 613 | plt.legend() 614 | 615 | plt.show() 616 | 617 | ##Figure 2: Reservoir Pressure Decline and Cumulative Volume Produced#### 618 | plt.figure(2, figsize = (6,4.5)) 619 | plt.subplot(211) 620 | plt.plot(sim_ts, sim_P_ress, 'k--', label = 'Reservoir Pressure') 621 | #plt.plot(m.time, P_wf.value, 'r--', label = r'$P_{wf}$') 622 | plt.ylabel('Pressure, psi') 623 | plt.legend() 624 | 625 | plt.subplot(212) 626 | plt.plot(sim_ts, sim_Vps, '--', label = 'Cumulative Volume Produced') 627 | plt.ylabel('Volume, STB') 628 | plt.xlabel('time, sec') 629 | plt.legend() 630 | plt.tight_layout() 631 | 632 | plt.show() 633 | 634 | ##Figure 3: NPV ######################################### 635 | plt.figure(3, figsize = (6,4.5)) 636 | plt.plot(sim_ts, sim_NPVs/(1e6), 'g:', label = 'NPV') 637 | plt.xlabel('time, sec') 638 | plt.ylabel('NPV, $ Millions') 639 | plt.legend() 640 | 641 | plt.show() 642 | 643 | ######################################################## 644 | 645 | #Figure 4# Work of Motor And 646 | plt.figure(4, figsize = (6,4.5)) 647 | plt.subplot(311) 648 | plt.plot(sim_ts,sim_W_rods, 'b-', label = 'Work Supplied by Motor' ) 649 | plt.ylabel('KiloWatts, KW') 650 | 651 | plt.subplot(312) 652 | plt.plot(sim_ts, sim_SPMs, 'r-', label = 'Input' ) 653 | plt.ylabel('SPM') 654 | 655 | plt.subplot(313) 656 | plt.plot(sim_ts, sim_P_wfs, 'r--', label = r'$P_{wf}$') 657 | plt.ylabel('FBHP, psi') 658 | plt.xlabel('time, sec') 659 | #plt.tight_layout() 660 | 661 | plt.show() 662 | 663 | ##Figure 5 -Doublet Test 664 | plt.figure(5, figsize = (6,4.5)) 665 | plt.subplot(211) 666 | plt.plot(sim_ts, sim_hstor, 'r--', label = 'height in annulus') 667 | plt.ylabel('height, ft') 668 | plt.legend() 669 | 670 | plt.subplot(212) 671 | plt.plot(sim_ts, sim_SPMs, 'b--', label = r'SPM') 672 | plt.ylabel('strokes/min') 673 | plt.xlabel('time, sec') 674 | plt.legend() 675 | 676 | plt.show() 677 | -------------------------------------------------------------------------------- /MHE_MPC.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Apr 6 13:20:42 2018 4 | 5 | @author: Brandon 6 | """ 7 | 8 | #from __future__ import division # compatibility with python 2.7 9 | from gekko import GEKKO 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | import time 13 | from mpl_toolkits.mplot3d.axes3d import Axes3D 14 | #%% 15 | #Define Timespace for simulation 16 | tf = 1 # sec, length of simulation 17 | npt = 20 # number of time discretizations 18 | nit = 1 # number of itterations to solve 19 | ti = np.linspace(0,tf,npt) # times for plotting 20 | 21 | 22 | # Define time space for MPC 23 | tf_mpc = 5 24 | npt_mpc = npt*tf_mpc 25 | 26 | 27 | # Define Rod Discretizations 28 | TVD_d = 4800 # ft, lenth of rod 29 | npx = 10 # number of rod discretizations 30 | dx = TVD_d/(npx-1) # #ft lenth of rod discretizations 31 | xi = np.linspace(0,TVD_d,npx) # possitions allong rod (for plotting) 32 | 33 | #Set Points 34 | SPM_in = np.ones(npt)*10 35 | 36 | 37 | #BuildModle#################################################################### 38 | mpc = GEKKO() 39 | sim = GEKKO() 40 | mhe = GEKKO() 41 | time_mpc = np.linspace(0,tf,npt) 42 | time_mpc = np.append(time_mpc, [1.1,1.4,2,2.5,3,3.5,4,4.5,5,5.5,6.5,7]) 43 | #Horizon Window 44 | mpc.time = time_mpc#np.linspace(0,tf_mpc,npt_mpc) 45 | sim.time = np.linspace(0,tf,npt) 46 | mhe.time = np.linspace(0,tf,npt) 47 | 48 | ################################ 49 | # Conventional Rod Pump Unit Geometry 50 | # API geometry dimension values 51 | Ag=210.0 52 | Cg=120.3 53 | Ig=120.0 54 | Pg=148.5 55 | Hg=237.88 56 | Gg=86.88 57 | Rg=47.0 58 | 59 | #lengths from FIG. 1 - Beam Pumping Unit Shown as a Four-Bar Linkage 60 | L_1 = Rg 61 | L_2 = np.sqrt((Hg-Gg)**2.0+Ig**2.0) 62 | L_3 = Cg 63 | L_4 = Pg 64 | L_5 = Ag 65 | 66 | starting_height = 3/4 67 | 68 | #Setpoints 69 | level_height = 3 70 | SP = starting_height*TVD_d-level_height 71 | dSP = 0.1 72 | 73 | 74 | 75 | #Simulation######################################## 76 | 77 | for m in [sim,mpc,mhe]: 78 | #Constants 79 | m.API = m.Const(value = 45) #API gravity of fluid, unitless 80 | m.c = m.Const(value = 0.000013) #Compressibility, psi^-1 81 | m.k = m.Const(value = 15) #Permeability, md 82 | m.Bo = m.Const(value = 1.2) #FVF, rb/STB 83 | m.A_d = m.FV(value = 2, ub = 8, lb = 1) #Drainage Area, Acres 84 | m.sw = m.Const(value = 0.2) #Water Saturation 85 | m.porosity = m.FV(value = 0.08, ub = .12, lb = 0.07) #Porosity, unitless 86 | m.gamma_E = m.Const(value = 1.78) #Euler Constant 87 | m.C_a = m.Const(value = 31.6) #Drainage Area Shape Factor (Circular) 88 | m.rw = m.Const(value = 0.328) #Welbore radius, ft 89 | m.S = m.FV(value = 0, ub = 10, lb = -5) #unitless 90 | m.u_visc = m.Const(value = 1.5) # Viscosity, cp 91 | m.h_pz = m.Const(value = 8) #pay zone thickness, ft 92 | m.D_t = m.Const(value = 2.5) # tubing diameter, in 93 | m.St_length = m.Const(value = 85) # rod pump stroke length, in 94 | 95 | m.g = m.Const(value = 32.2) # acceleration due to gravity, ft/s^3 96 | m.g_conv= m.Const(value = 32.2) # lbf conversion , lb-ft/s^2-lbf 97 | m.rho_r = m.Const(value = 490) # lbs/ft^3, density of rod steel 98 | m.rho_w = m.Const(value = 62.3 ) # lbs/ft^3, density of water at standard conditions 99 | m.a = m.Const(value =18996.06 ) # ft/s speed of sound in steel 100 | m.D_r = m.Const(value = 1.0) # in, diameter of rod string 101 | m.Ac = m.Const(value= m.D_r.value**2/4.0*np.pi) # in^2, cross sectional area of rod 102 | m.nu = m.Const(value = 0.01) # unitless, damping coefficient 103 | m.pi = m.Const(value=np.pi) 104 | m.E = m.Const(value = 32025000.0) # psi sucker rod modulus of elasticity 105 | m.alpha = m.Const(value = 0.0) # pump parameter, unitless 106 | m.beta = m.Const(value = 1.0) # pump parameter, unitless 107 | 108 | m.L_1 = m.Const(value =L_1) # unit geometry 109 | m.L_2 = m.Const(value =L_2) # unit geometry 110 | m.L_3 = m.Const(value =L_3) # unit geometry 111 | m.L_4 = m.Const(value =L_4) # unit geometry 112 | m.L_5 = m.Const(value =L_5) # unit geometry 113 | m.dx = m.Const(value = dx) # ft delta x 114 | 115 | #Prime Mover Constants (Torque Balance) 116 | m.tau_p = m.Const(value = 3) #tau 117 | m.k_gain = m.Const(value = 1) #one to one ratio between torque and SPM 118 | 119 | ##Economic 120 | m.Weight_lb_ft = m.Const(value = m.rho_r.value*m.Ac.value*m.g.value/m.g_conv/144) #Weight of rod string, lbf/ft 121 | m.Capex = m.Const(value = 200000) #Cost of Pumping Rod Unit,$? 122 | m.P_o = m.Const(value = 50) #Price of Oil, $/STB 123 | m.r = m.Const(value= .12/365) #Daily Discount Rate, % 124 | m.P_th = m.Const(value = 100) #tubing head pressure, psi 125 | m.TVD = m.Const(value = 4800) #true vertical depth, ft 126 | m.E_cost = m.Const(value = 0.13/3600) #Cost of Electricity, cents/Kws 127 | 128 | #Calculated Constants #DO NOT MODIFY# 129 | m.Wr = m.Const(value = m.TVD.value*m.Weight_lb_ft.value) #Weight of entire rod string, lbm 130 | m.D_a = m.Const(value = 2*12*m.rw.value) #Annulus Diameter, in 131 | m.gamma = m.Const(141.5/(m.API.value+131.5)) #Specific gravity of Fluid 132 | m.P_startpump = m.Const(value = 0.433*m.gamma.value*m.TVD.value) #Average Reservoir Pressure at Pump start up 133 | m.Pi = m.Const(value = .433*m.TVD.value) #Initial Reservoir Pressure, psi 134 | 135 | m.A_t = m.Const((np.pi/4)*m.D_t.value**2) #Cross sectional Area of tubing, in^2 136 | m.A_a = m.Const((np.pi/4)*m.D_a.value**2) #Cross Sectional Area of Annulus, in^2 137 | m.Wf = m.Const(value = m.TVD.value*m.rho_w.value*m.gamma.value*m.g.value/m.g_conv.value*(m.A_t.value-m.Ac.value)/144) # lbf, weight of fluid in tubing 138 | 139 | #MV's 140 | m.SPM_in = m.MV(value = 15, lb = 5, ub = 15) #Rod Pump Pumping Speed/Torque, spm 141 | 142 | #Variables 143 | m.V_i= m.Var(value = 7758*m.A_d.value*m.h_pz.value*m.porosity.value*(1-m.sw.value)/m.Bo.value) #OOIP, stb 144 | m.Vp = m.Var(value = m.V_i.value*(np.exp(m.c.value*(m.Pi.value-m.P_startpump.value))-1)) #initial volume produced prior stb 145 | 146 | if m == sim or m == mpc: 147 | m.h = m.CV(value = 1.0*m.TVD.value*starting_height) 148 | else: 149 | m.h = m.MV(value = 1.0*m.TVD.value*starting_height, lb = 0, ub = 4800) # Height, ft 150 | 151 | m.NPV = m.Var(value = -1.0*m.Capex.value) #Net Present Value, $ 152 | m.y = m.Var( lb = -1, ub = 1) # SIGN(x) 153 | m.sa = m.Var(value = 0, lb = 0) # slack variable a 154 | m.sb = m.Var(value = 0, lb = 0) # slack variable b 155 | m.tsi = m.Var(value = 0.0) # mulation time 156 | m.SPM = m.Var(value = 15) #SPM, strokes/min 157 | #omega = m.Var(value = 0) 158 | m.theta = m.Var(value = 0) # rad i.e sec^-1 crank angle of surface unit 159 | m.u = [m.SV(value = 9.22) for i in range(npx)] # relative position of each rod segment 160 | m.v = [m.Var(value = 0.0) for i in range(npx)] # velocity of reach rod segment 161 | m.f = [m.SV(value = 0.0) for i in range (npx)] # load at each rod segment 162 | m.P = m.Var(value = 1e-6) # unitless, load at the pump 163 | 164 | 165 | ## State Variables 166 | m.P_res = m.Var(value = m.P_startpump.value*1.0) #Current Reservoir Pressure , psi 167 | m.P_wf = m.Var(value = 0.433*m.gamma*m.h.value) #Bottomhole Flowing Pressure, psi 168 | m.q_in = m.Var(value = (1/86400)*m.k.value*m.h_pz.value*(m.P_res.value-m.P_wf.value)/(141.2*m.Bo.value*m.u_visc.value*((1/2)*np.log(4*m.A_d.value/(m.gamma_E.value*m.C_a.value*m.rw.value**2)) + m.S.value))) #IPR-VLP Flow rate, STB/s 169 | m.q_out = m.Var(value = 0) # Outgoing Flow Rate, STB/s 170 | m.t = m.Var(value = 0) #Time, days 171 | m.W_rod = m.Var(value = (1.0962)*m.q_out.value*(m.P_th.value-m.P_wf.value + .433*m.gamma.value*m.TVD.value) + (4.7053e-7)*m.Wr.value*m.St_length.value*m.SPM.value) #Work supplied by electric Motor, KW 172 | 173 | #Intermediates 174 | m.hs = m.Intermediate(m.sqrt(L_1**2 +L_2**2 + 2 *L_1 *L_2 *m.cos(m.theta))) 175 | 176 | #Equations 177 | ##AlgebraicEqns 178 | m.Equation(m.V_i == 7758*m.A_d*m.h_pz*m.porosity*(1-m.sw)/m.Bo) 179 | m.Equation(m.P_wf == 0.433*m.gamma*m.h) 180 | m.Equation(m.P_res == m.Pi-(1/m.c)*m.log((m.Vp/m.V_i)+1)) 181 | m.Equation(m.q_in == (1/86400)*m.k*m.h_pz*(m.P_res-m.P_wf)/(141.2*m.Bo*m.u_visc*((1/2)*m.log(4*m.A_d/(m.gamma_E*m.C_a*m.rw**2)) + m.S))) #STB/s 182 | m.Equation(m.W_rod == (1.0962)*m.q_out*(m.P_th-m.P_wf + .433*m.gamma*m.TVD) + (4.7053e-7)*m.Wr*m.St_length*m.SPM) 183 | 184 | #Prime Mover Equations- Torque Balance and Kinematic Eqns 185 | m.Equation(m.SPM.dt() == -(1/m.tau_p)*m.SPM + (m.k_gain/m.tau_p)*m.SPM_in) 186 | m.Equation((2*m.pi/60)*m.SPM == m.theta.dt()) 187 | 188 | m.Equation(m.u[0] == (1/12)*L_5*(m.asin(L_1*m.sin(m.theta)/m.hs)+m.acos((m.hs**2+L_3**2-L_4**2)/(2*L_3*m.hs)))) # position of polished rod, inches 189 | [m.Equation(m.v[i+1].dt()== m.a**2 * (m.u[i+2] - 2.0*m.u[i+1] + m.u[i])/m.dx**2 - m.pi*m.a*m.nu/(2.0*m.TVD)*m.v[i+1] - (1-m.rho_w*m.gamma/m.rho_r)*m.g) for i in range(npx-2) ]# wave equation 190 | m.Equation(m.q_out == m.A_t * m.u[-1].dt()*12/231/42 * (1+m.y)/2) # rate of fluid production, barrels/ 191 | 192 | # Equations for calculating rod loading 193 | # Load at surface 194 | m.Equation(m.f[0] == m.E*m.Ac*1/2/m.dx *(-m.u[2] + 4*m.u[1] -3*m.u[0])) 195 | # Load at pump 196 | m.Equation(m.f[npx-1] == m.E*m.Ac* m.P) 197 | # load at intermediate points 198 | [m.Equation(m.f[1+i] == m.E*m.Ac*1/2.0/dx*(m.u[i+2] - m.u[i])) for i in range(npx-2)] 199 | # pump boundary 200 | m.Equation( m.u[npx-1]*m.alpha + (m.u[npx-1] - m.u[npx-2])/dx == m.P) 201 | #add in signum for lifting and lowering conditions 202 | m.Equation(m.v[-1] == m.sb - m.sa ) 203 | m.Equation(m.P == -((m.Wf- (m.A_t - m.Ac)*m.P_wf)/m.E/m.Ac) * (1 + m.y)/2 ) # -P_wf*A_t 204 | 205 | 206 | ##DifferentialEans 207 | m.Equation(m.t.dt() == 1) 208 | m.Equation(m.Vp.dt() == m.q_in) 209 | m.Equation(m.NPV.dt() == (m.P_o*m.q_out-m.E_cost*m.W_rod)*m.exp(-m.r*m.t)) 210 | m.Equation(m.h.dt() == (1617/2)*(m.q_in - m.q_out)/(m.A_a -m.A_t)) 211 | m.Equation(m.tsi.dt()==1.0) # create time variable 212 | [m.Equation(m.u[i].dt()==m.v[i]) for i in range(npx)] # velocity of rod string 213 | 214 | # Set Objectives ################################################## 215 | m.Obj((m.sa*(1+m.y) + m.sb*(1-m.y))) # objective function to make signum work. 216 | 217 | #SetGlobalOptions(Simulation)############################################################## 218 | sim.options.IMODE = 5 # 4 = Dynamic Simulation (Seqential) 219 | sim.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 220 | sim.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 221 | sim.options.time_shift = npt-1 # time shift forward for multiple simulations 222 | sim.options.MAX_ITER = 450 223 | 224 | #SetLocalOptions############################################################### 225 | #N/A 226 | sim.SPM_in.FSTATUS = 1 # accept measurments 227 | sim.SPM_in.STATUS = 0 # don't let optimizer change (simulation) 228 | 229 | #MHE########################################################################### 230 | #Parameters (Holds Measured values from MHE) 231 | fm = mhe.Param(value = sim.f[0]) 232 | 233 | #SetGlobalOptions(MHE)########################################################## 234 | mhe.options.IMODE = 5 # 4 = Dynamic Simulation (Seqential) 235 | mhe.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 236 | mhe.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 237 | mhe.options.time_shift = npt-1 # time shift forward for multiple simulations 238 | mhe.options.MAX_ITER = 700 239 | mhe.Obj((mhe.f[0] - fm)**2) 240 | 241 | #SetLocalOptions (MHE)############################################################### 242 | ##FV #Variable to estimate 243 | mhe.h.FSTATUS = 0 244 | mhe.h.STATUS = 1 245 | mhe.h.DMAX = 0.5 246 | 247 | #MV 248 | mhe.SPM_in.FSTATUS = 1 249 | mhe.SPM_in.STATUS = 0 250 | 251 | #SetGlobalOptions(MPC)########################################################## 252 | mpc.options.IMODE = 6 # 4 = Dynamic Simulation (Seqential) 253 | mpc.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 254 | mpc.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 255 | mpc.options.time_shift = npt-1 # time shift forward for multiple simulations 256 | mpc.options.MAX_ITER = 450 257 | mpc.options.CV_TYPE = 1 258 | 259 | #SetLocalOptions############################################################### 260 | ##FV 261 | mpc.SPM_in.STATUS = 1 262 | mpc.SPM_in.FSTATUS = 0 # cound be meausured, but we are setting it 263 | mpc.SPM_in.DMAX = .05 # maximum change in SPM per cycle 264 | #mpc.SPM_in.DCOST = .01 265 | 266 | # CV's 267 | mpc.h.STATUS = 1 # control h 268 | mpc.h.FSTATUS = 1 # accept measurements 269 | mpc.h.TR_INIT = 2 # create trajectory to setpoint 270 | mpc.h.TAU = .1 # time constant of trajectory to h set point 271 | 272 | #Solve######################################################################### 273 | #%% 274 | # Solve the simulation in a loop to simulate a longer horizon 275 | loops = 180 # number of steps forward in time 276 | res = {} 277 | solve_stat = np.zeros(loops) 278 | t_cycle = 0.0 279 | #Initialize Storage Values 280 | sim_ts = np.ones(npt)*sim.tsi.value # simulation time storage 281 | sim_hstor = np.ones(npt)*sim.h.value # height of fluid in annulus storage 282 | sim_q_ins= np.ones(npt)*sim.q_in.value # reservoir influx storage 283 | sim_q_outs = np.ones(npt)*sim.q_out.value # production rate storage 284 | sim_P_ress = np.ones(npt)*sim.P_res.value # reservoir pressure storage 285 | sim_Vps = np.ones(npt)*sim.Vp.value # cumulative volume produced storage 286 | sim_NPVs = np.ones(npt)*sim.NPV.value # NPV storage 287 | sim_W_rods = np.ones(npt)*sim.W_rod.value # work of rod (work to lift fluid) storage 288 | sim_SPMs = np.ones(npt)*sim.SPM_in.value # Strokes per minute/ Torque storage Set Points 289 | sim_SPMr = np.ones(npt)*sim.SPM.value #SPM storage 290 | sim_thetas = np.ones(npt)*sim.theta.value#Theta storage 291 | sim_P_wfs = np.ones(npt)*sim.P_wf.value # bottom hole pressure storage 292 | sim_ys = np.ones(npt)*sim.y.value # sign of du/dt storage 293 | 294 | 295 | #MHE Storage 296 | mpc_ts = np.empty(0) # simulation time storage 297 | mhe_us = [np.array(mpc.u[i].value) for i in range(npx)] # u relative position storage 298 | mhe_vs = [np.array(mpc.v[i].value) for i in range(npx)] 299 | mhe_fs = [np.array(mpc.f[i].value) for i in range(npx)] # dynamic load storage 300 | mpc_hstor = np.empty(0)# height of fluid in annulus storage 301 | mpc_q_ins= np.empty(0) # reservoir influx storage 302 | mpc_q_outs = np.empty(0) # production rate storage 303 | mpc_P_ress = np.empty(0) # reservoir pressure storage 304 | mpc_Vps = np.empty(0) # cumulative volume produced storage 305 | mpc_NPVs = np.empty(0) # NPV storage 306 | mpc_W_rods =np.empty(0) # work of rod (work to lift fluid) storage 307 | mpc_SPMs = np.empty(0) # Strokes per minute/ Torque storage Set Points 308 | mpc_SPMr = np.empty(0) #SPM storage 309 | mpc_thetas = np.empty(0)#Theta storage 310 | mpc_P_wfs = np.empty(0) # bottom hole pressure storage 311 | mpc_ys = np.empty(0) # sign of du/dt storage 312 | mpc_Skins = np.empty(0) #Skin storage 313 | mpc_porosity = np.empty(0) 314 | mpc_A_d = np.empty(0) 315 | mhe_Skins = np.empty(0) #Skin storage 316 | mhe_hstor = np.empty(0)# height of fluid in annulus storage 317 | mpc_Skins = np.empty(0) 318 | mpc_Skinss = np.empty(0) 319 | mpc_h = np.empty(0) 320 | mpc_hss = np.empty(0) 321 | ############################################################### 322 | 323 | for i in range(loops): 324 | 325 | # simulate system for 1 second 326 | sim.solve() 327 | if i == 0: 328 | # Create and store results 329 | sim_ts = np.array(sim.tsi.value) # simulation time storage 330 | sim_us = [np.array(sim.u[i].value) for i in range(npx)] # u relative position storage 331 | sim_vs = [np.array(sim.v[i].value) for i in range(npx)] 332 | sim_fs = [np.array(sim.f[i].value) for i in range(npx)] # dynamic load storage 333 | sim_hstor = np.array(sim.h.value) # height of fluid in annulus storage 334 | sim_q_ins= np.array(sim.q_in.value) # reservoir influx storage 335 | sim_q_outs = np.array(sim.q_out.value) # production rate storage 336 | sim_P_ress = np.array(sim.P_res.value) # reservoir pressure storage 337 | sim_Vps = np.array(sim.Vp.value) # cumulative volume produced storage 338 | sim_NPVs = np.array(sim.NPV.value) # NPV storage 339 | sim_W_rods = np.array(sim.W_rod.value) # work of rod (work to lift fluid) storage 340 | sim_SPMs = np.array(sim.SPM_in.value) # Strokes per minute/ Torque storage Set Points 341 | sim_SPMr = np.array(sim.SPM.value) #SPM storage 342 | sim_thetas = np.array(sim.theta.value)#Theta storage 343 | sim_P_wfs = np.array(sim.P_wf.value) # bottom hole pressure storage 344 | sim_ys = np.array(sim.y.value) # sign of du/dt storage 345 | 346 | elif i>0: 347 | sim_ts = np.append(sim_ts,sim.tsi.value) # simulation time storage 348 | sim_us = [np.append(sim_us[i],sim.u[i].value) for i in range(npx)] # u relative position storage 349 | sim_vs = [np.append(sim_vs[i],sim.v[i].value) for i in range(npx)] 350 | sim_fs = [np.append(sim_fs[i],sim.f[i].value) for i in range(npx)] # dynamic load storage 351 | sim_hstor = np.append(sim_hstor,sim.h.value) # height of fluid in annulus storage 352 | sim_q_ins= np.append(sim_q_ins,sim.q_in.value) # reservoir influx storage 353 | sim_q_outs = np.append(sim_q_outs,sim.q_out.value) # production rate storage 354 | sim_P_ress = np.append(sim_P_ress,sim.P_res.value) # reservoir pressure storage 355 | sim_Vps = np.append(sim_Vps,sim.Vp.value) # cumulative volume produced storage 356 | sim_NPVs = np.append(sim_NPVs,sim.NPV.value) # NPV storage 357 | sim_W_rods = np.append(sim_W_rods,sim.W_rod.value) # work of rod (work to lift fluid) storage 358 | sim_SPMs = np.append(sim_SPMs,sim.SPM_in.value) # Strokes per minute storage 359 | sim_SPMr = np.append(sim_SPMr,sim.SPM.value) #Strokes per minute storage 360 | sim_thetas = np.append(sim_thetas,sim.theta.value) 361 | sim_P_wfs = np.append(sim_P_wfs,sim.P_wf.value) # bottom hole pressure storage 362 | sim_ys = np.append(sim_ys,sim.y.value) # sign of du/dt storage 363 | solve_stat[i] = t_cycle 364 | ##MHE################################################################## 365 | #Insert Measurements 366 | fm.value = sim.f[0].value 367 | #um.value = sim.u[0].value 368 | #hm.value = sim.h.value 369 | 370 | mhe.SPM_in.value = sim.SPM_in.value 371 | 372 | #Solve 373 | t_start = time.time() 374 | mhe.solve() 375 | 376 | #Pass values to MPC 377 | mpc.h.MEAS = mhe.h.NEWVAL 378 | 379 | #Store new values for plotting 380 | mhe_hstor = np.append(mhe_hstor,mhe.h.value) 381 | 382 | ##MPC ##################################################################### 383 | 384 | mpc.h.SP = SP # ft 385 | mpc.h.SPHI = SP + dSP# + 1 386 | mpc.h.SPLO = SP - dSP#- 387 | mpc.h.TAU = .1 388 | mpc.solve() 389 | t_end = time.time() 390 | t_cycle = t_end - t_start 391 | if(mpc.options.APPSTATUS ==1): 392 | # store spm 393 | sim.SPM_in.value = mpc.SPM_in.value[0:20] 394 | 395 | else: 396 | #sim.SPM_in.value = 397 | exit 398 | mpc.h.TR_INIT = 1 399 | if i ==0: 400 | mpc_hs = np.array(mpc.h.value[0:npt]) 401 | elif i>0: 402 | mpc_hs = np.append(mpc_hs, mpc.h.value[0:npt]) 403 | ####################################################################### 404 | 405 | # Plotting 406 | plt.clf() 407 | ax=plt.subplot(311) 408 | ax.grid() 409 | plt.plot(sim_ts[0:i*npt],sim_SPMs[0:i*npt],'ro',label='SPM Set Point') 410 | plt.plot(sim_ts[0:i*npt],sim_SPMr[0:i*npt],'bo',label='SPM') 411 | plt.ylabel('Strokes per Minute') 412 | plt.legend(loc=2) 413 | ax=plt.subplot(312) 414 | ax.grid() 415 | plt.plot(sim_ts[0:i*npt],sim_hstor[0:i*npt],'k-',label= 'height') 416 | plt.plot(sim_ts[0:i*npt], np.ones(i*npt)*SP, label = 'height SP') 417 | #plt.plot(ts[0:i*npt], mpc_hs[0:npt*i], label = 'mpc height') 418 | plt.ylabel('Annular Fluid Height') 419 | plt.legend(loc='best') 420 | ax = plt.subplot(313) 421 | ax.grid() 422 | plt.plot(sim_ts[0:i*npt], sim_q_outs[0:i*npt], label = 'q_out') 423 | plt.plot(sim_ts[0:i*npt], sim_q_ins[0:i*npt], label = 'q_in') 424 | plt.legend() 425 | plt.ylabel('Flow Rate, STB/s') 426 | plt.xlabel('Time (sec)') 427 | plt.draw() 428 | #============================================================================== 429 | # ax = plt.subplot(414) 430 | # ax.grid() 431 | # plt.plot(sim_ts[0:i*npt], mhe_hstor[0:i*npt], label = 'mhe_height') 432 | # plt.plot(sim_ts[0:i*npt], mpc_hs[0:i*npt], label = 'mpc_height') 433 | # plt.legend() 434 | # plt.ylabel('Annular Fluid Height') 435 | # plt.xlabel('Time (sec)') 436 | # plt.draw() 437 | #============================================================================== 438 | plt.pause(0.02) 439 | 440 | 441 | #%% 442 | 443 | res['solve_stat'] = solve_stat 444 | res['ts' ] = sim_ts 445 | res['us' ] = sim_us 446 | res['vs' ] = sim_vs 447 | res['fs'] = sim_fs 448 | res['hstor' ] = sim_hstor 449 | res[ 'q_ins' ] = sim_q_ins 450 | res[ 'q_outs' ] = sim_q_outs 451 | res['P_ress' ] = sim_P_ress 452 | res['Vps' ] = sim_Vps 453 | res[ 'NPVs' ] = sim_NPVs 454 | res['W_rods' ] = sim_W_rods 455 | res['SPMs'] = sim_SPMs 456 | res['SPMr'] = sim_SPMr 457 | res['thetas'] = sim_thetas 458 | res['P_wfs'] = sim_P_wfs 459 | res[ 'ys'] = sim_ys 460 | res['h_SP'] = np.ones(loops*npt)*SP 461 | res['mhe_hstor']= mhe_hstor 462 | #%% 463 | np.save('MPC_MHE_Control_Results_20npt_10npx.npy', res) 464 | #%% 465 | # Load dictionary of results 466 | res = np.load('MPC_MHE_Control_Results_20npt_10npx.npy').item() 467 | 468 | #%% Plotting from dictionary 469 | 470 | 471 | plt.figure() 472 | plt.rcParams['xtick.labelsize'] = 12 473 | plt.rcParams['ytick.labelsize'] = 12 474 | ax=plt.subplot(311) 475 | ax.grid() 476 | plt.plot(res['ts'], res['SPMs'], 'r--', label=r'$T_{net}$ (ft-lb)')#, s = 4, c='b' ) # 'ro', for latex 477 | plt.plot(res['ts'], res['SPMr'],'b-', label=r'Actual')#, s = 4, c = 'r') #'bo', 478 | plt.ylabel('SPM', fontsize = 12) 479 | plt.legend(loc= 1,fontsize = 12) 480 | plt.ylim(5,16) 481 | plt.xlim(0,180) 482 | ax=plt.subplot(312) 483 | ax.grid() 484 | plt.plot(res['ts'], res['hstor'],'k-',label= 'Actual') 485 | plt.plot(res['ts'], np.ones(np.size(res['ts']))*(sim.TVD.value*3/4 -3), label = 'SP') # fix 486 | #plt.plot(ts[0:i*npt], mpc_hs[0:npt*i], label = 'mpc height') 487 | plt.ylabel('Fluid Level (ft)', fontsize = 12) 488 | plt.legend(loc=1,fontsize = 12) 489 | plt.xlim(0,180) 490 | ax = plt.subplot(313) 491 | ax.grid() 492 | plt.plot(res['ts'], res['q_outs'], label = r'$q_{out}$') 493 | plt.plot(res['ts'], res['q_ins'], label = r'$q_{in}$') 494 | plt.legend(loc = 1,fontsize = 12) 495 | plt.ylabel('Flow (STB/s)', fontsize = 12) 496 | plt.xlabel('Time (seconds)', fontsize = 12) 497 | plt.xlim(0,180) 498 | 499 | #ax = plt.subplot(414) 500 | #ax.grid() 501 | #plt.plot(res['ts'], res['mhe_hstor'], label = 'mhe height') 502 | ##plt.plot(sim_ts[0:i*npt], mpc_hs[0:i*npt], label = 'mpc height') 503 | #plt.plot(res['ts'],res['hstor'],'k-',label= 'true height') 504 | #ax.legend(loc='lower center', bbox_to_anchor=(.89, .1), ncol=1, fancybox=True, shadow=True, fontsize=12) 505 | ##plt.legend() 506 | #plt.ylabel('Height', fontsize=12) 507 | #plt.xlabel('Time (sec)',fontsize=12) 508 | # 509 | 510 | #plt.draw() 511 | 512 | 513 | #ax = plt.subplot(414) 514 | #ax.grid() 515 | #plt.plot(sim_ts[0:i*npt], mhe_hstor[0:i*npt], label = 'mhe height') 516 | ##plt.plot(sim_ts[0:i*npt], mpc_hs[0:i*npt], label = 'mpc height') 517 | #plt.plot(sim_ts[0:i*npt],sim_hstor[0:i*npt],'k-',label= 'true height') 518 | #ax.legend(loc='lower center', bbox_to_anchor=(.89, .1), ncol=1, fancybox=True, shadow=True, fontsize=16) 519 | ##plt.legend() 520 | #plt.ylabel('Height', fontsize=22) 521 | #plt.xlabel('Time (sec)',fontsize=22) 522 | #plt.legend(fontsize = 12) 523 | #plt.ylabel('Fluid Level (ft)', fontsize = 12) 524 | #plt.xlabel('Time (seconds)', fontsize = 12) 525 | 526 | #plt.tight_layout() 527 | plt.savefig('MPC_MHE_Control_10_Sec_Horizon.eps', transparent = True, dpi = 1200) 528 | plt.show() 529 | 530 | #%% 531 | # timing figure 532 | plt.rcParams['xtick.labelsize'] = 12 533 | plt.rcParams['ytick.labelsize'] = 12 534 | plt.figure() 535 | plt.plot(np.linspace(1,180,179), res['solve_stat'][1:],'r-',label='Solve Time') 536 | plt.plot(np.linspace(1,loops,loops-1), np.ones(loops-1)*np.average(res['solve_stat'][1:]),'b--', label = 'Average') 537 | plt.plot(np.linspace(1,loops,loops-1), np.ones(loops-1),'k:', label = 'Real Time') 538 | plt.xlabel('Control Cycle', fontsize = 12) 539 | plt.ylabel('Computation Time (seconds)', fontsize = 12) 540 | plt.legend(fontsize = 12) 541 | plt.ylim(0,60) 542 | plt.xlim(0,180) 543 | plt.savefig('MPC_MHE_Simulation_Timing.eps', dpi = 1200, transparent = True) 544 | plt.show() 545 | 546 | 547 | 548 | 549 | #%% ### Plotting after having ran it 550 | ax=plt.subplot(411) 551 | ax.grid() 552 | plt.plot(sim_ts[0:i*npt],sim_SPMs[0:i*npt],'ro',label='Motor Torque') 553 | plt.plot(sim_ts[0:i*npt],sim_SPMr[0:i*npt],'bo',label='SPM') 554 | plt.ylabel('SPM', fontsize=22) 555 | ax.legend(loc='lower center', bbox_to_anchor=(.88, .13), ncol=1, fancybox=True, shadow=True, fontsize=16) 556 | #plt.legend(loc=2) 557 | ax=plt.subplot(412) 558 | ax.grid() 559 | plt.plot(sim_ts[0:i*npt],sim_hstor[0:i*npt],'k-',label= 'height') 560 | plt.plot(sim_ts[0:i*npt], np.ones(i*npt)*(sim.TVD.value*3/4 -3), label = 'height SP') 561 | #plt.plot(ts[0:i*npt], mpc_hs[0:npt*i], label = 'mpc height') 562 | plt.ylabel('Height', fontsize=22) 563 | ax.legend(loc='lower center', bbox_to_anchor=(.90, .13), ncol=1, fancybox=True, shadow=True, fontsize=16) 564 | #plt.legend(loc='best') 565 | ax = plt.subplot(413) 566 | ax.grid() 567 | plt.plot(sim_ts[0:i*npt], sim_q_outs[0:i*npt], label = r'$q_{out}$') 568 | plt.plot(sim_ts[0:i*npt], sim_q_ins[0:i*npt], label = r'$q_{in}$') 569 | ax.legend(loc='lower center', bbox_to_anchor=(.92, .13), ncol=1, fancybox=True, shadow=True, fontsize=16) 570 | #plt.legend() 571 | plt.ylabel('STB/s', fontsize=22) 572 | #plt.xlabel('Time (sec)') 573 | plt.draw() 574 | ax = plt.subplot(414) 575 | ax.grid() 576 | plt.plot(sim_ts[0:i*npt], mhe_hstor[0:i*npt], label = 'mhe height') 577 | #plt.plot(sim_ts[0:i*npt], mpc_hs[0:i*npt], label = 'mpc height') 578 | plt.plot(sim_ts[0:i*npt],sim_hstor[0:i*npt],'k-',label= 'true height') 579 | ax.legend(loc='lower center', bbox_to_anchor=(.89, .1), ncol=1, fancybox=True, shadow=True, fontsize=16) 580 | #plt.legend() 581 | plt.ylabel('Height', fontsize=22) 582 | plt.xlabel('Time (sec)',fontsize=22) 583 | plt.draw() 584 | plt.pause(0.02) 585 | 586 | 587 | ############################################################### 588 | #============================================================================== 589 | # 590 | # for i in range(loops): 591 | # 592 | # 593 | # mhe.solve() 594 | # mhe_ts = np.append(mhe_ts,mhe.tsi.value) # simulation time storage 595 | # mhe_us = [np.append(mhe_us[i],mhe.u[i].value) for i in range(npx)] # u relative position storage 596 | # mhe_vs = [np.append(mhe_vs[i],mhe.v[i].value) for i in range(npx)] 597 | # mhe_fs = [np.append(mhe_fs[i],mhe.f[i].value) for i in range(npx)] # dynamic load storage 598 | # mhe_hstor = np.append(mhe_hstor,mhe.h.value) # height of fluid in annulus storage 599 | # mhe_q_ins= np.append(mhe_q_ins,mhe.q_in.value) # reservoir influx storage 600 | # mhe_q_outs = np.append(mhe_q_outs,mhe.q_out.value) # production rate storage 601 | # mhe_P_ress = np.append(mhe_P_ress,mhe.P_res.value) # reservoir pressure storage 602 | # mhe_Vps = np.append(mhe_Vps,mhe.Vp.value) # cumulative volume produced storage 603 | # mhe_NPVs = np.append(mhe_NPVs,mhe.NPV.value) # NPV storage 604 | # mhe_W_rods = np.append(mhe_W_rods,mhe.W_rod.value) # work of rod (work to lift fluid) storage 605 | # mhe_SPMs = np.append(mhe_SPMs,mhe.SPM_in.value) # Strokes per minute storage 606 | # mhe_SPMr = np.append(mhe_SPMr,mhe.SPM.value) #Strokes per minute storage 607 | # mhe_thetas = np.append(mhe_thetas,mhe.theta.value) 608 | # mhe_P_wfs = np.append(mhe_P_wfs,mhe.P_wf.value) # bottom hole pressure storage 609 | # mhe_ys = np.append(mhe_ys,mhe.y.value) # sign of du/dt storage 610 | #============================================================================== 611 | 612 | 613 | #PlotResults################################################################### 614 | #============================================================================== 615 | # #%% 616 | # ## Figure 1 617 | # plt.figure(1, figsize = (6,4.5)) 618 | # plt.subplot(211) 619 | # plt.plot(mhe_ts, mhe_hstor, 'r--', label = 'height in annulus') 620 | # plt.ylabel('height, ft') 621 | # plt.legend() 622 | # 623 | # plt.subplot(212) 624 | # plt.plot(mhe_ts, mhe_q_ins, 'b--', label = r'$q_{in}$') 625 | # plt.plot(mhe_ts, mhe_q_outs, 'g--', label = r'$q_{out}$') 626 | # plt.ylabel('Flow Rate, STB/s') 627 | # plt.xlabel('time, sec') 628 | # plt.legend() 629 | # 630 | # plt.show() 631 | # 632 | # ##Figure 2 633 | # plt.figure(2, figsize = (6,4.5)) 634 | # plt.subplot(211) 635 | # plt.plot(mhe_ts, mhe_P_ress, 'k--', label = 'Reservoir Pressure') 636 | # #plt.plot(m.time, P_wf.value, 'r--', label = r'$P_{wf}$') 637 | # plt.ylabel('Pressure, psi') 638 | # plt.legend() 639 | # 640 | # plt.subplot(212) 641 | # plt.plot(mhe_ts, mhe_Vps, '--', label = 'Cumulative Volume Produced') 642 | # plt.ylabel('Volume, STB') 643 | # plt.xlabel('time, sec') 644 | # plt.legend() 645 | # plt.tight_layout() 646 | # 647 | # plt.show() 648 | # 649 | # ##Figure 3 650 | # plt.figure(3, figsize = (6,4.5)) 651 | # plt.plot(mhe_ts, mhe_NPVs/(1e6), 'g:', label = 'NPV') 652 | # plt.xlabel('time, sec') 653 | # plt.ylabel('NPV, $ Millions') 654 | # plt.legend() 655 | # 656 | # plt.show() 657 | # 658 | # #Figure 4 659 | # plt.figure(4, figsize = (6,4.5)) 660 | # plt.subplot(311) 661 | # plt.plot(mhe_ts,mhe_W_rods, 'b-', label = 'Work Supplied by Motor' ) 662 | # plt.ylabel('KiloWatts, KW') 663 | # 664 | # plt.subplot(312) 665 | # plt.plot(mhe_ts, mhe_SPMs, 'r-', label = 'Work Supplied by Motor' ) 666 | # plt.ylabel('SPM') 667 | # 668 | # plt.subplot(313) 669 | # plt.plot(mhe_ts, mhe_P_wfs, 'r--', label = r'$P_{wf}$') 670 | # plt.ylabel('FBHP, psi') 671 | # plt.xlabel('time, sec') 672 | # #plt.tight_layout() 673 | # 674 | # plt.show() 675 | # 676 | # ##Figure 5 -Doublet Test 677 | # plt.figure(5, figsize = (6,4.5)) 678 | # plt.subplot(211) 679 | # plt.plot(mhe_ts, mhe_hstor, 'r--', label = 'height in annulus') 680 | # plt.ylabel('height, ft') 681 | # plt.legend() 682 | # 683 | # plt.subplot(212) 684 | # plt.plot(mhe_ts, mhe_SPMs, 'b--', label = r'SPM') 685 | # plt.ylabel('strokes/min') 686 | # plt.xlabel('time, sec') 687 | # plt.legend() 688 | # 689 | # plt.show() 690 | # 691 | # # store results in to structure for 3-d plotting 692 | # for i in range(npx): 693 | # if i ==0: 694 | # mhe_ustor = np.array([mhe_us[i]]) 695 | # mhe_tstor = np.array([mhe_ts]) 696 | # else: 697 | # mhe_ustor = np.vstack([mhe_ustor,mhe_us[i]]) 698 | # mhe_tstor = np.vstack([mhe_tstor,mhe_ts]) 699 | # 700 | # for i in range(len(mhe_ts)): 701 | # if i == 0: 702 | # xstor = xi 703 | # else: 704 | # xstor = np.vstack([xstor,xi]) 705 | # x = xstor.T 706 | # t = mhe_tstor 707 | # mhe_ustor = np.array(mhe_ustor) 708 | # 709 | # fig = plt.figure() 710 | # ax = fig.add_subplot(1,1,1,projection='3d') 711 | # ax.set_xlabel('Distance x') 712 | # ax.set_ylabel('Time t') 713 | # p = ax.plot_wireframe(x,t,mhe_ustor,rstride=1,cstride=1) 714 | # fig.show() 715 | # 716 | # plt.figure() 717 | # [plt.scatter(mhe_ts,mhe_us[i], label ='u' +str(i)) for i in range(npx)] 718 | # #plt.plot(m.time,u[-1],'r--') 719 | # plt.legend() 720 | # plt.show() 721 | # 722 | # # Plot surface dynagraph () 723 | # plt.figure() 724 | # plt.plot((mhe_us[0]- np.min(mhe_us[0]))*12,-mhe_fs[0] + mhe.TVD.value*mhe.Ac.value*mhe.rho_r.value/144,label = 'Surface Dynagraph') # dynamic plus static load 725 | # plt.legend() 726 | # plt.xlabel('Position (in)') 727 | # plt.ylabel('Load (lbf)') 728 | # plt.show() 729 | # 730 | # # plot pump dynagraph 731 | # plt.figure() 732 | # plt.plot((mhe_us[npx-1]-np.min(mhe_us[npx-1]))*12,-mhe_fs[npx-1], label = 'Pump Dynagraph') 733 | # plt.xlabel('Position (in)') 734 | # plt.ylabel('Load (lbf)') 735 | # plt.legend() 736 | # plt.show() 737 | # 738 | # # plot pump position vs. time 739 | # plt.figure() 740 | # plt.plot(mhe_ts,mhe_us[-1], label = 'Pump Position') 741 | # plt.plot(mhe_ts,mhe_vs[-1], label ='Pump Velocity') 742 | # plt.plot(mhe_ts,np.array(mhe_ys)*10, label = 'Sign(V)') 743 | # plt.legend() 744 | # plt.show() 745 | # 746 | # plt.figure() 747 | # plt.plot(mhe_ts,-mhe_fs[-1], label = 'Pump Load (lbf)') 748 | # plt.legend() 749 | # plt.xlabel('Time (s)') 750 | # plt.ylabel('Load (lbf)') 751 | # plt.legend() 752 | # plt.tight_layout() 753 | # 754 | # plt.show() 755 | # 756 | # #Plot SPM Dynamics 757 | # plt.figure() 758 | # plt.plot(mhe_ts,mhe_SPMs) 759 | # plt.plot(mhe_ts,mhe_SPMr) 760 | # plt.ylabel('SPM') 761 | # plt.xlabel('time,s') 762 | # 763 | # plt.show() 764 | # 765 | # #Plot theta Dynamics (radians) 766 | # plt.figure() 767 | # plt.plot(mhe_ts,mhe_thetas) 768 | # plt.xlabel('time, s') 769 | # plt.ylabel('radians') 770 | # plt.show() 771 | #============================================================================== 772 | -------------------------------------------------------------------------------- /Simulation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Apr 6 13:20:42 2018 4 | 5 | @author: Brandon 6 | """ 7 | 8 | #from __future__ import division # compatibility with python 2.7 9 | from gekko import GEKKO 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | import time 13 | from mpl_toolkits.mplot3d.axes3d import Axes3D 14 | #%% 15 | 16 | # make an empty dictionary to store values 17 | res = {} 18 | 19 | # start time 20 | start_time = time.time() 21 | 22 | #BuildModle#################################################################### 23 | sim = GEKKO() 24 | 25 | 26 | #Define Timespace 27 | tf = 1 # sec, length of simulation 28 | npt = 30 # number of time discretizations 29 | nit = 1 # number of itterations to solve 30 | ti = np.linspace(0,tf,npt) # times for plotting 31 | 32 | sim.time = np.linspace(0,tf,npt) 33 | 34 | # Define Rod Discretizations 35 | TVD_d = 4800 # ft, lenth of rod 36 | npx = 30 # number of rod discretizations 37 | dx = TVD_d/(npx-1) # #ft lenth of rod discretizations 38 | xi = np.linspace(0,TVD_d,npx) # possitions allong rod (for plotting) 39 | 40 | ################################ 41 | # Conventional Rod Pump Unit Geometry 42 | # API geometry dimension values 43 | Ag=210.0 44 | Cg=120.3 45 | Ig=120.0 46 | Pg=148.5 47 | Hg=237.88 48 | Gg=86.88 49 | Rg=47.0 50 | 51 | #lengths from FIG. 1 - Beam Pumping Unit Shown as a Four-Bar Linkage 52 | L_1 = Rg 53 | L_2 = np.sqrt((Hg-Gg)**2.0+Ig**2.0) 54 | L_3 = Cg 55 | L_4 = Pg 56 | L_5 = Ag 57 | 58 | #Simulation######################################## 59 | 60 | #Constants 61 | sim.API = sim.Const(value = 45) #API gravity of fluid, unitless 62 | sim.c = sim.Const(value = 0.000013) #Compressibility, psi^-1 63 | sim.k = sim.Const(value = 15) #Permeability, md 64 | sim.Bo = sim.Const(value = 1.2) #FVF, rb/STB 65 | sim.A_d = sim.Const(value = 2) #Drainage Area, Acres 66 | sim.sw = sim.Const(value = 0.2) #Water Saturation 67 | sim.porosity = sim.Const(value = 0.08) #Porosity, unitless 68 | sim.gamma_E = sim.Const(value = 1.78) #Euler Constant 69 | sim.C_a = sim.Const(value = 31.6) #Drainage Area Shape Factor (Circular) 70 | sim.rw = sim.Const(value = 0.328) #Welbore radius, ft 71 | sim.S = sim.Const(value = 0) #unitless 72 | sim.u_visc = sim.Const(value = 1.5) # Viscosity, cp 73 | sim.h_pz = sim.Const(value = 8) #pay zone thickness, ft 74 | sim.D_t = sim.Const(value = 2.5) # tubing diameter, in 75 | sim.St_length = sim.Const(value = 85) # rod pump stroke length, in 76 | 77 | sim.g = sim.Const(value = 32.2) # acceleration due to gravity, ft/s^3 78 | sim.g_conv= sim.Const(value = 32.2) # lbf conversion , lb-ft/s^2-lbf 79 | sim.rho_r = sim.Const(value = 490) # lbs/ft^3, density of rod steel 80 | sim.rho_w = sim.Const(value = 62.3 ) # lbs/ft^3, density of water at standard conditions 81 | sim.a = sim.Const(value =18996.06 ) # ft/s speed of sound in steel 82 | sim.D_r = sim.Const(value = 1.0) # in, diameter of rod string 83 | sim.Ac = sim.Const(value= sim.D_r.value**2/4.0*np.pi) # in^2, cross sectional area of rod 84 | sim.nu = sim.Const(value = 0.01) # unitless, damping coefficient 85 | sim.pi = sim.Const(value=np.pi) 86 | sim.E = sim.Const(value = 32025000.0) # psi sucker rod modulus of elasticity 87 | sim.alpha = sim.Const(value = 0.0) # pump parameter, unitless 88 | sim.beta = sim.Const(value = 1.0) # pump parameter, unitless 89 | 90 | sim.L_1 = sim.Const(value =L_1) # unit geometry 91 | sim.L_2 = sim.Const(value =L_2) # unit geometry 92 | sim.L_3 = sim.Const(value =L_3) # unit geometry 93 | sim.L_4 = sim.Const(value =L_4) # unit geometry 94 | sim.L_5 = sim.Const(value =L_5) # unit geometry 95 | sim.dx = sim.Const(value = dx) # ft delta x 96 | 97 | #Prime Mover Constants (Torque Balance) 98 | sim.tau_p = sim.Const(value = 3) #tau 99 | sim.k_gain = sim.Const(value = 1) #one to one ratio between torque and SPM 100 | 101 | ##Economic 102 | sim.Weight_lb_ft = sim.Const(value = sim.rho_r.value*sim.Ac.value*sim.g.value/sim.g_conv/144) #Weight of rod string, lbf/ft 103 | sim.Capex = sim.Const(value = 200000) #Cost of Pumping Rod Unit,$? 104 | sim.P_o = sim.Const(value = 50) #Price of Oil, $/STB 105 | sim.r = sim.Const(value= .12/365) #Daily Discount Rate, % 106 | sim.P_th = sim.Const(value = 100) #tubing head pressure, psi 107 | sim.TVD = sim.Const(value = 4800) #true vertical depth, ft 108 | sim.E_cost = sim.Const(value = 0.13/3600) #Cost of Electricity, cents/Kws 109 | 110 | #Calculated Constants #DO NOT MODIFY# 111 | sim.Wr = sim.Const(value = sim.TVD.value*sim.Weight_lb_ft.value) #Weight of entire rod string, lbm 112 | sim.D_a = sim.Const(value = 2*12*sim.rw.value) #Annulus Diameter, in 113 | sim.gamma = sim.Const(141.5/(sim.API.value+131.5)) #Specific gravity of Fluid 114 | sim.P_startpump = sim.Const(value = 0.433*sim.gamma.value*sim.TVD.value) #Average Reservoir Pressure at Pump start up 115 | sim.Pi = sim.Const(value = .433*sim.TVD.value) #Initial Reservoir Pressure, psi 116 | sim.V_i = sim.Const(value = 7758*sim.A_d.value*sim.h_pz.value*sim.porosity.value*(1-sim.sw.value)/sim.Bo.value) #OOIP, stb 117 | 118 | 119 | sim.A_t = sim.Const((np.pi/4)*sim.D_t.value**2) #Cross sectional Area of tubing, in^2 120 | sim.A_a = sim.Const((np.pi/4)*sim.D_a.value**2) #Cross Sectional Area of Annulus, in^2 121 | sim.Wf = sim.Const(value = sim.TVD.value*sim.rho_w.value*sim.gamma.value*sim.g.value/sim.g_conv.value*(sim.A_t.value-sim.Ac.value)/144) # lbf, weight of fluid in tubing 122 | 123 | #MV's 124 | sim.SPM_in = sim.MV(value = 10, lb = 5, ub = 15) #Rod Pump Pumping Speed/Torque, spm 125 | 126 | #Variables 127 | sim.Vp = sim.Var(value = sim.V_i.value*(np.exp(sim.c.value*(sim.Pi.value-sim.P_startpump.value))-1)) #initial volume produced prior stb 128 | sim.h = sim.Var(value = 1.0*sim.TVD.value * 3 / 4) # Height, ft 129 | sim.NPV = sim.Var(value = -1.0*sim.Capex.value) #Net Present Value, $ 130 | sim.y = sim.Var( lb = -1, ub = 1) # SIGN(x) 131 | sim.sa = sim.Var(value = 0, lb = 0) # slack variable a 132 | sim.sb = sim.Var(value = 0, lb = 0) # slack variable b 133 | sim.tsi = sim.Var(value = 0.0) # simulation time 134 | sim.SPM = sim.Var(value = 10) #SPM, strokes/min 135 | #omega = m.Var(value = 0) 136 | sim.theta = sim.Var(value = 0) # rad i.e sec^-1 crank angle of surface unit 137 | sim.u = [sim.Var(value = 9.22,name = 'un'+str(i)) for i in range(npx) ] # relative position of each rod segment 138 | sim.v = [sim.Var(value = 0.0, name = 'vn'+str(i)) for i in range(npx)] # velocity of reach rod segment 139 | sim.f = [sim.Var(value = 0.0, name = 'fn'+str(i)) for i in range (npx)] # load at each rod segment 140 | sim.P = sim.Var(value = 1e-6) # unitless, load at the pump 141 | 142 | 143 | ## State Variables 144 | sim.P_res = sim.Var(value = sim.P_startpump.value*1.0) #Current Reservoir Pressure , psi 145 | sim.P_wf = sim.Var(value = 0.433*sim.gamma*sim.h.value) #Bottomhole Flowing Pressure, psi 146 | sim.q_in = sim.Var(value = (1/86400)*sim.k.value*sim.h_pz.value*(sim.P_res.value-sim.P_wf.value)/(141.2*sim.Bo.value*sim.u_visc.value*((1/2)*np.log(4*sim.A_d.value/(sim.gamma_E.value*sim.C_a.value*sim.rw.value**2)) + sim.S.value))) #IPR-VLP Flow rate, STB/s 147 | sim.q_out = sim.Var(value = 0) # Outgoing Flow Rate, STB/s 148 | sim.t = sim.Var(value = 0) #Time, days 149 | sim.W_rod = sim.Var(value = (1.0962)*sim.q_out.value*(sim.P_th.value-sim.P_wf.value + .433*sim.gamma.value*sim.TVD.value) + (4.7053e-7)*sim.Wr.value*sim.St_length.value*sim.SPM.value) #Work supplied by electric Motor, KW 150 | 151 | #Intermediates 152 | sim.hs = sim.Intermediate(sim.sqrt(L_1**2 +L_2**2 + 2 *L_1 *L_2 *sim.cos(sim.theta))) 153 | sim.V_rp = sim.Intermediate((1/9702)*(np.pi/4)*sim.D_t**2*sim.St_length) #Volume Extracted per stroke length, STB 154 | 155 | #Equations 156 | ##AlgebraicEqns 157 | sim.Equation(sim.P_wf == 0.433*sim.gamma*sim.h) 158 | sim.Equation(sim.P_res == sim.Pi-(1/sim.c)*sim.log((sim.Vp/sim.V_i)+1)) 159 | sim.Equation(sim.q_in == (1/86400)*sim.k*sim.h_pz*(sim.P_res-sim.P_wf)/(141.2*sim.Bo*sim.u_visc*((1/2)*sim.log(4*sim.A_d/(sim.gamma_E*sim.C_a*sim.rw**2)) + sim.S))) #STB/s 160 | sim.Equation(sim.W_rod == (1.0962)*sim.q_out*(sim.P_th-sim.P_wf + .433*sim.gamma*sim.TVD) + (4.7053e-7)*sim.Wr*sim.St_length*sim.SPM) 161 | 162 | #Prime Mover Equations- Torque Balance and Kinematic Eqns 163 | sim.Equation(sim.SPM.dt() == -(1/sim.tau_p)*sim.SPM + (sim.k_gain/sim.tau_p)*sim.SPM_in) 164 | sim.Equation((2*sim.pi/60)*sim.SPM == sim.theta.dt()) 165 | 166 | #m.Equation(theta ==2.5118541087922712 +tsi*SPM_in * 2.0 * 3.147 / 60.0) # convert time to angle in radians 167 | #m.Equation(SPM == omega/(2*pi)/60) 168 | 169 | sim.Equation(sim.u[0] == (1/12)*L_5*(sim.asin(L_1*sim.sin(sim.theta)/sim.hs)+sim.acos((sim.hs**2+L_3**2-L_4**2)/(2*L_3*sim.hs)))) # position of polished rod, inches 170 | [sim.Equation(sim.v[i+1].dt()== sim.a**2 * (sim.u[i+2] - 2.0*sim.u[i+1] + sim.u[i])/sim.dx**2 - sim.pi*sim.a*sim.nu/(2.0*sim.TVD)*sim.v[i+1] - (1-sim.rho_w*sim.gamma/sim.rho_r)*sim.g) for i in range(npx-2) ]# wave equation 171 | sim.Equation(sim.q_out == sim.A_t * sim.u[-1].dt()*12/231/42 * (1+sim.y)/2) # rate of fluid production, barrels/ 172 | #m.Equation(q_out == (1/60)*V_rp*SPM) 173 | 174 | # Equations for calculating rod loading 175 | # Load at surface 176 | sim.Equation(sim.f[0] == sim.E*sim.Ac*1/2/sim.dx *(-sim.u[2] + 4*sim.u[1] -3*sim.u[0])) 177 | # Load at pump 178 | #m.Equation(f[npx-1] == E*Ac* 1/2.0/dx *(3*u[npx-1] - 4*u[npx-2] + u[npx-3])) 179 | sim.Equation(sim.f[npx-1] == sim.E*sim.Ac* sim.P) 180 | # load at intermediate points 181 | [sim.Equation(sim.f[1+i] == sim.E*sim.Ac*1/2.0/dx*(sim.u[i+2] - sim.u[i])) for i in range(npx-2)] 182 | # pump boundary 183 | sim.Equation( sim.u[npx-1]*sim.alpha + (sim.u[npx-1] - sim.u[npx-2])/dx == sim.P) 184 | #add in signum for lifting and lowering conditions 185 | sim.Equation(sim.v[-1] == sim.sb - sim.sa ) 186 | sim.Equation(sim.P == -((sim.Wf- (sim.A_t - sim.Ac)*sim.P_wf)/sim.E/sim.Ac) * (1 + sim.y)/2 ) # -P_wf*A_t 187 | 188 | 189 | ##DifferentialEans 190 | sim.Equation(sim.t.dt() == 1) 191 | sim.Equation(sim.Vp.dt() == sim.q_in) 192 | sim.Equation(sim.NPV.dt() == (sim.P_o*sim.q_out-sim.E_cost*sim.W_rod)*sim.exp(-sim.r*sim.t)) 193 | sim.Equation(sim.h.dt() == (1617/2)*(sim.q_in - sim.q_out)/(sim.A_a -sim.A_t)) 194 | sim.Equation(sim.tsi.dt()==1.0) # create time variable 195 | [sim.Equation(sim.u[i].dt()==sim.v[i]) for i in range(npx)] # velocity of rod string 196 | 197 | # Set Objectives ################################################## 198 | sim.Obj((sim.sa*(1+sim.y) + sim.sb*(1-sim.y))) # objective function to make signum work. 199 | sim.Equation((sim.sa*(1+sim.y) + sim.sb*(1-sim.y))<= 1e-6) 200 | 201 | #%% 202 | #SetGlobalOptions Simulator ############################################################## 203 | sim.options.IMODE = 5 # 4 = Dynamic Simulation (Seqential) 204 | sim.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 205 | sim.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 206 | sim.options.time_shift = npt-1 # time shift forward for multiple simulations 207 | sim.options.MAX_ITER = 450 208 | 209 | #SetLocalOptions Simulator############################################################### 210 | sim.SPM_in.FSTATUS = 0#1 # accept measurments 211 | sim.SPM_in.STATUS = 0 # don't let optimizer change (simulation) 212 | 213 | #%% 214 | # Simulate the application in loop 215 | 216 | loops = 1800 # number of steps forward in time 217 | sim_time = tf * loops # Total length of simulation horizon (sec) 218 | 219 | time_loops1800 = np.zeros(loops) # store the time to do each solution 220 | 221 | plt.figure() 222 | plt.ion() 223 | plt.show() 224 | 225 | for i in range(loops): 226 | 227 | # start loop time 228 | start_loop_time = time.time() 229 | 230 | ############################# 231 | 232 | #Doublet Test ############### 233 | 234 | if i > loops*(3/4): 235 | sim.SPM_in.VALUE = 10 236 | 237 | if i > loops*(2/4) and i < loops*(3/4): 238 | sim.SPM_in.VALUE = 5 239 | 240 | if i > (1/4)*loops and i < loops*(2/4): 241 | sim.SPM_in.VALUE = 15 242 | 243 | if i < (1/4)*loops or i < 10:#10: # TODO: if rerunnign this probably change this value of 61 back to 10 244 | sim.SPM_in.VALUE = 10#10 245 | 246 | ############################# 247 | 248 | # simulate system for 1 second 249 | sim.solve() 250 | if i == 0: 251 | # Create and store results 252 | ts = np.array(sim.tsi.value) # simulation time storage 253 | us = [np.array(sim.u[i].value) for i in range(npx)] # u relative position storage 254 | vs = [np.array(sim.v[i].value) for i in range(npx)] 255 | fs = [np.array(sim.f[i].value) for i in range(npx)] # dynamic load storage 256 | hstor = np.array(sim.h.value) # height of fluid in annulus storage 257 | q_ins= np.array(sim.q_in.value) # reservoir influx storage 258 | q_outs = np.array(sim.q_out.value) # production rate storage 259 | P_ress = np.array(sim.P_res.value) # reservoir pressure storage 260 | Vps = np.array(sim.Vp.value) # cumulative volume produced storage 261 | NPVs = np.array(sim.NPV.value) # NPV storage 262 | W_rods = np.array(sim.W_rod.value) # work of rod (work to lift fluid) storage 263 | SPMs = np.array(sim.SPM_in.value) # Strokes per minute/ Torque storage Set Points 264 | SPMr = np.array(sim.SPM.value) #SPM storage 265 | thetas = np.array(sim.theta.value)#Theta storage 266 | P_wfs = np.array(sim.P_wf.value) # bottom hole pressure storage 267 | ys = np.array(sim.y.value) # sign of du/dt storage 268 | 269 | elif i>0: 270 | ts = np.append(ts,sim.tsi.value) # simulation time storage 271 | us = [np.append(us[i],sim.u[i].value) for i in range(npx)] # u relative position storage 272 | vs = [np.append(vs[i],sim.v[i].value) for i in range(npx)] 273 | fs = [np.append(fs[i],sim.f[i].value) for i in range(npx)] # dynamic load storage 274 | hstor = np.append(hstor,sim.h.value) # height of fluid in annulus storage 275 | q_ins= np.append(q_ins,sim.q_in.value) # reservoir influx storage 276 | q_outs = np.append(q_outs,sim.q_out.value) # production rate storage 277 | P_ress = np.append(P_ress,sim.P_res.value) # reservoir pressure storage 278 | Vps = np.append(Vps,sim.Vp.value) # cumulative volume produced storage 279 | NPVs = np.append(NPVs,sim.NPV.value) # NPV storage 280 | W_rods = np.append(W_rods,sim.W_rod.value) # work of rod (work to lift fluid) storage 281 | SPMs = np.append(SPMs,sim.SPM_in.value) # Strokes per minute storage 282 | SPMr = np.append(SPMr,sim.SPM.value) #Strokes per minute storage 283 | thetas = np.append(thetas,sim.theta.value) 284 | P_wfs = np.append(P_wfs,sim.P_wf.value) # bottom hole pressure storage 285 | ys = np.append(ys,sim.y.value) # sign of du/dt storage 286 | 287 | # Plotting 288 | plt.clf() 289 | ax=plt.subplot(311) 290 | ax.grid() 291 | plt.plot(ts[0:i*npt],SPMs[0:i*npt],'ro',label='Motor Torque') 292 | plt.plot(ts[0:i*npt],SPMr[0:i*npt],'bo',label='SPM') 293 | plt.ylabel('Strokes per Minute') 294 | plt.legend(loc=2) 295 | ax=plt.subplot(312) 296 | ax.grid() 297 | plt.plot(ts[0:i*npt],hstor[0:i*npt],'k-',label= 'height') 298 | plt.plot(ts[0:i*npt], np.ones(i*npt)*(sim.TVD.value*3/4 -3), label = 'height SP') 299 | plt.ylabel('Annular Fluid Height') 300 | plt.legend(loc='best') 301 | ax = plt.subplot(313) 302 | ax.grid() 303 | plt.plot(ts[0:i*npt], q_outs[0:i*npt], label = 'q_out') 304 | plt.plot(ts[0:i*npt], q_ins[0:i*npt], label = 'q_in') 305 | plt.legend() 306 | plt.ylabel('Flow Rate, STB/s') 307 | plt.xlabel('Time (sec)') 308 | plt.draw() 309 | plt.pause(0.02) 310 | ############################################################### 311 | # end loop time 312 | end_loop_time = time.time() 313 | time_loops1800[i] = end_loop_time - start_loop_time 314 | 315 | #PlotResults################################################################### 316 | #%% 317 | ## Figure 1 318 | plt.figure(figsize = (6,4.5)) 319 | plt.subplot(211) 320 | plt.plot(ts, hstor, 'r--', label = 'height in annulus') 321 | plt.ylabel('height, ft') 322 | plt.legend() 323 | 324 | plt.subplot(212) 325 | plt.plot(ts, q_ins, 'b--', label = r'$q_{in}$') 326 | plt.plot(ts, q_outs, 'g--', label = r'$q_{out}$') 327 | plt.ylabel('Flow Rate, STB/s') 328 | plt.xlabel('time, sec') 329 | plt.legend() 330 | 331 | plt.show() 332 | 333 | ##Figure 2 334 | plt.figure(figsize = (6,4.5)) 335 | plt.subplot(211) 336 | plt.plot(ts, P_ress, 'k--', label = 'Reservoir Pressure') 337 | #plt.plot(m.time, P_wf.value, 'r--', label = r'$P_{wf}$') 338 | plt.ylabel('Pressure, psi') 339 | plt.legend() 340 | 341 | plt.subplot(212) 342 | plt.plot(ts, Vps, '--', label = 'Cumulative Volume Produced') 343 | plt.ylabel('Volume, STB') 344 | plt.xlabel('time, sec') 345 | plt.legend() 346 | plt.tight_layout() 347 | 348 | plt.show() 349 | 350 | ##Figure 3 351 | plt.figure(figsize = (6,4.5)) 352 | plt.plot(ts, NPVs/(1e6), 'g:', label = 'NPV') 353 | plt.xlabel('time, sec') 354 | plt.ylabel('NPV, $ Millions') 355 | plt.legend() 356 | 357 | plt.show() 358 | 359 | #Figure 4 360 | plt.figure(figsize = (6,4.5)) 361 | plt.subplot(311) 362 | plt.plot(ts,W_rods, 'b-', label = 'Work Supplied by Motor' ) 363 | plt.ylabel('KiloWatts, KW') 364 | 365 | plt.subplot(312) 366 | plt.plot(ts, SPMs, 'r-', label = 'Work Supplied by Motor' ) 367 | plt.ylabel('SPM') 368 | 369 | plt.subplot(313) 370 | plt.plot(ts, P_wfs, 'r--', label = r'$P_{wf}$') 371 | plt.ylabel('FBHP, psi') 372 | plt.xlabel('time, sec') 373 | #plt.tight_layout() 374 | 375 | plt.show() 376 | 377 | ##Figure 5 -Doublet Test 378 | plt.figure(figsize = (6,4.5)) 379 | plt.subplot(211) 380 | plt.plot(ts, hstor, 'r--', label = 'height in annulus') 381 | plt.ylabel('height, ft') 382 | plt.legend() 383 | 384 | plt.subplot(212) 385 | plt.plot(ts, SPMs, 'b--', label = r'SPM') 386 | plt.ylabel('strokes/min') 387 | plt.xlabel('time, sec') 388 | plt.legend() 389 | 390 | plt.show() 391 | 392 | # store results in to structure for 3-d plotting 393 | for i in range(npx): 394 | if i ==0: 395 | ustor = np.array([us[i]]) 396 | tstor = np.array([ts]) 397 | else: 398 | ustor = np.vstack([ustor,us[i]]) 399 | tstor = np.vstack([tstor,ts]) 400 | 401 | for i in range(len(ts)): 402 | if i == 0: 403 | xstor = xi 404 | else: 405 | xstor = np.vstack([xstor,xi]) 406 | x = xstor.T 407 | t = tstor 408 | ustor = np.array(ustor) 409 | 410 | fig = plt.figure() 411 | ax = fig.add_subplot(1,1,1,projection='3d') 412 | ax.set_xlabel('Distance x') 413 | ax.set_ylabel('Time t') 414 | p = ax.plot_wireframe(x,t,ustor,rstride=1,cstride=1) 415 | fig.show() 416 | 417 | plt.figure() 418 | [plt.scatter(ts,us[i], label ='u' +str(i)) for i in range(npx)] 419 | #plt.plot(m.time,u[-1],'r--') 420 | plt.legend() 421 | plt.show() 422 | 423 | # Plot surface dynagraph () 424 | plt.figure() 425 | plt.plot((us[0]- np.min(us[0]))*12,-fs[0],label = 'Surface Dynagraph') # dynamic plus static load #+ sim.TVD.value*sim.Ac.value*sim.rho_r.value/144 426 | plt.legend() 427 | plt.xlabel('Position (in)') 428 | plt.ylabel('Load (lbf)') 429 | plt.show() 430 | 431 | # plot pump dynagraph 432 | plt.figure() 433 | plt.plot((us[npx-1]-np.min(us[npx-1]))*12,-fs[npx-1], label = 'Pump Dynagraph') 434 | plt.xlabel('Position (in)') 435 | plt.ylabel('Load (lbf)') 436 | plt.legend() 437 | plt.show() 438 | 439 | # plot pump position vs. time 440 | plt.figure() 441 | plt.plot(ts,us[-1], label = 'Pump Position') 442 | plt.plot(ts,vs[-1], label ='Pump Velocity') 443 | plt.plot(ts,np.array(ys)*10, label = 'Sign(V)') 444 | plt.legend() 445 | plt.show() 446 | 447 | plt.figure() 448 | plt.plot(ts,-fs[-1], label = 'Pump Load (lbf)') 449 | plt.legend() 450 | plt.xlabel('Time (s)') 451 | plt.ylabel('Load (lbf)') 452 | plt.legend() 453 | plt.tight_layout() 454 | 455 | plt.show() 456 | 457 | #Plot SPM Dynamics 458 | plt.figure() 459 | plt.plot(ts,SPMs) 460 | plt.plot(ts,SPMr) 461 | plt.ylabel('SPM') 462 | plt.xlabel('time,s') 463 | 464 | plt.show() 465 | 466 | #Plot theta Dynamics (radians) 467 | plt.figure() 468 | plt.plot(ts,thetas) 469 | plt.xlabel('time, s') 470 | plt.ylabel('radians') 471 | plt.show() 472 | 473 | # endtime 474 | end_time = time.time() 475 | total_time1800 = end_time - start_time 476 | 477 | #%% # updated graphs for putting in the paper 478 | 479 | # Plot surface dynagraph () where it works 480 | plt.figure() 481 | plt.plot((us[0][-600:]- np.min(us[0][-600:]))*12,-fs[0][-600:],label = 'Surface Dynagraph') # dynamic plus static load #+ sim.TVD.value*sim.Ac.value*sim.rho_r.value/144 482 | plt.legend() 483 | plt.xlabel('Position (in)') 484 | plt.ylabel('Load (lbf)') 485 | plt.show() 486 | 487 | # plot pump dynagraph where it works 488 | plt.figure() 489 | plt.plot((us[npx-1][-600:]-np.min(us[npx-1][-600:]))*12,-fs[npx-1][-600:], label = 'Pump Dynagraph') 490 | plt.xlabel('Position (in)') 491 | plt.ylabel('Load (lbf)') 492 | plt.legend() 493 | plt.show() 494 | 495 | # plot pump position vs. time 496 | plt.figure() 497 | plt.plot(ts,us[-1], label = 'Pump Position') 498 | plt.plot(ts,vs[-1], label ='Pump Velocity') 499 | plt.plot(ts,np.array(ys)*10, label = 'Sign(V)') 500 | plt.legend() 501 | plt.show() 502 | 503 | # time for pump dynagraph 504 | plt.figure() 505 | plt.plot(ts,-fs[-1], label = 'Pump Load (lbf)') 506 | plt.legend() 507 | plt.xlabel('Time (s)') 508 | plt.ylabel('Load (lbf)') 509 | plt.legend() 510 | plt.tight_layout() 511 | plt.show() 512 | 513 | # time for surface dynagraph 514 | plt.figure() 515 | plt.plot(ts,-fs[0], label = 'Pump Load (lbf)') 516 | plt.legend() 517 | plt.xlabel('Time (s)') 518 | plt.ylabel('Load (lbf)') 519 | plt.legend() 520 | plt.tight_layout() 521 | plt.show() 522 | 523 | #%% Store Results in a dictionary 524 | 525 | res['ts' ] = ts 526 | res['us' ] = us 527 | res['vs' ] = vs 528 | res['fs' ] = fs 529 | res['hstor' ] = hstor 530 | res['q_ins' ] = q_ins 531 | res['q_outs' ] = q_outs 532 | res['P_ress' ] = P_ress 533 | res['Vps' ] = Vps 534 | res['NPVs' ] = NPVs 535 | res['W_rods' ] = W_rods 536 | res['SPMs' ] = SPMs 537 | res['SPMr' ] = SPMr 538 | res['thetas' ] = thetas 539 | res['P_wfs' ] = P_wfs 540 | res['ys' ] = ys 541 | res['times' ] = time_loops1800 542 | res['total_time'] = total_time1800 543 | 544 | np.save('Simulation_data_' + str(loops) + 's.npy', res) 545 | 546 | #%% Load results from the dictionary 547 | 548 | res = np.load('Simulation_data_1800s.npy').item() # res = np.load('Simulation_data_1800s.npy').item() 549 | loops = 1800 550 | 551 | #Define Timespace 552 | tf = 1 # sec, length of simulation 553 | npt = 30 # number of time discretizations 554 | nit = 1 # number of itterations to solve 555 | ti = np.linspace(0,tf,npt) # times for plotting 556 | 557 | # Define Rod Discretizations 558 | TVD_d = 4800 # ft, lenth of rod 559 | npx = 30 # number of rod discretizations 560 | dx = TVD_d/(npx-1) # #ft lenth of rod discretizations 561 | xi = np.linspace(0,TVD_d,npx) # possitions allong rod (for plotting) 562 | 563 | fig = plt.figure() # see line 673 564 | 565 | #%% plot using the dictionary 566 | # figure 1 567 | 568 | plt.figure() 569 | plt.rcParams['xtick.labelsize'] = 12 570 | plt.rcParams['ytick.labelsize'] = 12 571 | plt.rcParams['axes.formatter.useoffset'] = False 572 | plt.subplot(211) 573 | plt.plot(res['ts'], res['hstor'], 'r--', label = 'Fluid Level (ft)') 574 | plt.ylabel('Height (ft)', fontsize = 12) 575 | plt.legend(loc='best', fontsize = 12) 576 | 577 | plt.subplot(212) 578 | plt.plot(res['ts'], res['q_ins'], 'b--', label = r'$q_{in}$') 579 | plt.plot(res['ts'], res['q_outs'], 'g--', label = r'$q_{out}$') 580 | plt.ylabel('Flow (STB/s)', fontsize = 12) 581 | plt.xlabel('Time (seconds)', fontsize = 12) 582 | plt.legend(loc='best', fontsize = 12) 583 | plt.tight_layout() 584 | 585 | plt.savefig('fig_1.eps', dpi = 1200, Transparent = True) 586 | plt.show() 587 | 588 | #Figure 2 589 | 590 | plt.figure() 591 | plt.rcParams['xtick.labelsize'] = 12 592 | plt.rcParams['ytick.labelsize'] = 12 593 | plt.subplot(211) 594 | plt.plot(res['ts'], res['P_ress'], 'k--', label = 'Reservoir Pressure') 595 | #plt.plot(m.time, P_wf.value, 'r--', label = r'$P_{wf}$') 596 | plt.ylabel('Pressure (psi)', fontsize = 12) 597 | plt.legend(loc='best', fontsize = 12) 598 | 599 | plt.subplot(212) 600 | plt.plot(res['ts'], res['Vps'], '--', label = 'Cumulative Volume Produced') 601 | plt.ylabel('Volume (STB)', fontsize = 12) 602 | plt.xlabel('Time (seconds)', fontsize = 12) 603 | plt.legend(loc='best', fontsize = 12) 604 | plt.tight_layout() 605 | 606 | plt.savefig('fig_2.eps', dpi = 1200, Transparent = True) 607 | plt.show() 608 | 609 | #Figure 3 610 | 611 | plt.figure() 612 | plt.plot(res['ts'], res['NPVs']/(1e6), 'g:', label = 'NPV') 613 | plt.xlabel('Time (seconds)', fontsize = 12) 614 | plt.ylabel('NPV ($ Millions)', fontsize = 12) 615 | plt.legend(loc='best', fontsize = 12) 616 | plt.tight_layout() 617 | plt.savefig('fig_3.eps', dpi = 1200, Transparent = True) 618 | plt.show() 619 | 620 | #%% Figure 4 621 | plt.figure() 622 | plt.rcParams['xtick.labelsize'] = 12 623 | plt.rcParams['ytick.labelsize'] = 12 624 | plt.rcParams['axes.formatter.useoffset'] = False 625 | plt.subplot(311) 626 | plt.plot(res['ts'],res['W_rods'], 'b-', label = 'Lifting Power' ) 627 | plt.ylabel('KiloWatts (kW)', fontsize = 12) 628 | plt.legend(loc='best', fontsize = 12) 629 | plt.xlim(0,loops) 630 | 631 | plt.subplot(312) 632 | plt.plot(res['ts'], res['SPMs'], 'r--', label = r'$T_{net}$') 633 | # add Tnet 634 | plt.plot(res['ts'], res['SPMr'], 'b-', label = r'$SPM$') 635 | # 636 | plt.ylabel('SPM', fontsize = 12) 637 | plt.legend(loc='upper right', fontsize = 12) 638 | plt.xlim(0,loops) 639 | 640 | plt.subplot(313) 641 | plt.plot(res['ts'], res['P_wfs'], 'r--', label = r'$P_{wf}$') 642 | plt.ylabel('FBHP (psi)', fontsize = 12) 643 | plt.xlabel('Time (seconds)', fontsize = 12) 644 | plt.legend(loc='best', fontsize = 12) 645 | plt.xlim(0,loops) 646 | plt.tight_layout() 647 | 648 | plt.savefig('fig_4.eps', dpi = 1200, Transparent = True) 649 | plt.show() 650 | #%% 651 | 652 | # Figure 5 -Doublet Test 653 | 654 | plt.figure() 655 | plt.rcParams['xtick.labelsize'] = 12 656 | plt.rcParams['ytick.labelsize'] = 12 657 | plt.rcParams['axes.formatter.useoffset'] = False 658 | plt.subplot(211) 659 | plt.plot(res['ts'], res['hstor'], 'r--', label = 'Height in Annulus') 660 | plt.ylabel('Height (ft)', fontsize = 12) 661 | plt.legend(loc='best', fontsize = 12) 662 | 663 | plt.subplot(212) 664 | plt.plot(res['ts'], res['SPMs'], 'b--', label = r'$T_{net}$') 665 | plt.ylabel(r'$T_{net} (ft-lbs)$', fontsize = 12) 666 | plt.xlabel('Time (seconds)', fontsize = 12) 667 | plt.legend(loc='best', fontsize = 12) 668 | 669 | plt.tight_layout() 670 | plt.savefig('fig_5.eps', dpi = 1200, Transparent = True) 671 | plt.show() 672 | 673 | # Figure 6 674 | 675 | # store results in to structure for 3-d plotting 676 | for i in range(npx): 677 | if i ==0: 678 | ustor = np.array([res['us'][i]]) # TODO: check and fix? 679 | tstor = np.array([res['ts']]) 680 | else: 681 | ustor = np.vstack([ustor,res['us'][i]]) 682 | tstor = np.vstack([tstor,res['ts']]) 683 | 684 | for i in range(len(res['ts'])): 685 | if i == 0: 686 | xstor = xi 687 | else: 688 | xstor = np.vstack([xstor,xi]) 689 | x = xstor.T 690 | t = tstor 691 | ustor = np.array(ustor) 692 | 693 | fig # note it takes a long time to make this graph 694 | plt.rcParams['xtick.labelsize'] = 12 695 | plt.rcParams['ytick.labelsize'] = 12 696 | ax = fig.add_subplot(1,1,1,projection='3d') 697 | ax.set_xlabel('Distance (ft)') 698 | ax.set_ylabel('Time (seconds)') 699 | ax.set_zlabel('Position (ft)') 700 | p = ax.plot_wireframe(x,t,ustor - np.min(ustor),rstride=1,cstride=1) 701 | fig.savefig('fig_6.eps', dpi = 1200, Transparent = True) 702 | fig.show() 703 | 704 | #figure 7 705 | 706 | plt.figure() 707 | plt.rcParams['xtick.labelsize'] = 12 708 | plt.rcParams['ytick.labelsize'] = 12 709 | [plt.scatter(res['ts'],res['us'][i], label ='u' + str(i)) for i in range(npx)] 710 | #plt.plot(m.time,u[-1],'r--') 711 | plt.legend(loc='best', fontsize = 12) 712 | plt.savefig('fig_7.eps', dpi = 1200, Transparent = True) 713 | plt.show() 714 | 715 | # figure 8 716 | 717 | # Plot surface dynagraph () 718 | plt.figure() 719 | plt.rcParams['xtick.labelsize'] = 12 720 | plt.rcParams['ytick.labelsize'] = 12 721 | plt.plot((res['us'][0]- np.min(res['us'][0]))*12,-res['fs'][0],label = 'Surface Dynagraph') # dynamic plus static load #+ sim.TVD.value*sim.Ac.value*sim.rho_r.value/144 722 | plt.legend(loc='best', fontsize = 12) 723 | plt.xlabel('Position (in)', fontsize = 12) 724 | plt.ylabel('Load (lbf)', fontsize = 12) 725 | plt.savefig('fig_8.eps', dpi = 1200, Transparent = True) 726 | plt.show() 727 | 728 | # figure 9 729 | 730 | # plot pump dynagraph 731 | plt.figure() 732 | plt.rcParams['xtick.labelsize'] = 12 733 | plt.rcParams['ytick.labelsize'] = 12 734 | plt.plot((res['us'][npx-1]-np.min(res['us'][npx-1]))*12,-res['fs'][npx-1], label = 'Pump Dynagraph') 735 | plt.xlabel('Position (in)', fontsize = 12) 736 | plt.ylabel('Load (lbf)', fontsize = 12) 737 | plt.legend(loc='best', fontsize = 12) 738 | plt.savefig('fig_9.eps', dpi = 1200, Transparent = True) 739 | plt.show() 740 | 741 | # figure 10 742 | 743 | # plot pump position vs. time 744 | plt.figure() 745 | plt.rcParams['xtick.labelsize'] = 12 746 | plt.rcParams['ytick.labelsize'] = 12 747 | plt.plot(res['ts'],res['us'][-1], label = 'Pump Position') 748 | plt.plot(res['ts'],res['vs'][-1], label ='Pump Velocity') 749 | plt.plot(res['ts'],np.array(res['ys'])*10, label = 'Sign(V)') 750 | plt.legend(loc='best', fontsize = 12) 751 | fig.savefig('fig_10.eps', dpi = 1200, Transparent = True) 752 | plt.show() 753 | 754 | # figure 11 755 | 756 | plt.figure() 757 | plt.rcParams['xtick.labelsize'] = 12 758 | plt.rcParams['ytick.labelsize'] = 12 759 | plt.plot(res['ts'],-res['fs'][-1], label = 'Pump Load (lbf)') 760 | plt.legend(loc='best', fontsize = 12) 761 | plt.xlabel('Time (seconds)', fontsize = 12) 762 | plt.ylabel('Load (lbf)', fontsize = 12) 763 | plt.legend(loc='best', fontsize = 12) 764 | plt.tight_layout() 765 | 766 | plt.savefig('fig_11.eps', dpi = 1200, Transparent = True) 767 | plt.show() 768 | 769 | # figure 12 770 | 771 | #Plot SPM Dynamics 772 | plt.figure() 773 | plt.rcParams['xtick.labelsize'] = 12 774 | plt.rcParams['ytick.labelsize'] = 12 775 | plt.plot(res['ts'],res['SPMs']) 776 | plt.plot(res['ts'],res['SPMr']) 777 | plt.ylabel('SPM', fontsize = 12) 778 | plt.xlabel('Time (seconds)', fontsize = 12) 779 | 780 | plt.savefig('fig_12.eps', dpi = 1200, Transparent = True) 781 | plt.show() 782 | 783 | # figure 13 784 | 785 | #Plot theta Dynamics (Radians) 786 | plt.figure() 787 | plt.rcParams['xtick.labelsize'] = 12 788 | plt.rcParams['ytick.labelsize'] = 12 789 | plt.plot(res['ts'],res['thetas']) 790 | plt.xlabel('Time (seconds)', fontsize = 12) 791 | plt.ylabel('Radians', fontsize = 12) 792 | plt.savefig('fig_13.eps', dpi = 1200, Transparent = True) 793 | plt.show() 794 | 795 | #%% updated graphs for putting in the paper 796 | 797 | # figure 14 798 | 799 | # Plot surface dynagraph () where it works 800 | plt.figure() 801 | plt.rcParams['xtick.labelsize'] = 12 802 | plt.rcParams['ytick.labelsize'] = 12 803 | plt.plot((res['us'][0][-600:]- np.min(res['us'][0][-600:]))*12,-res['fs'][0][-600:],label = 'Surface Dynagraph') # dynamic plus static load #+ sim.TVD.value*sim.Ac.value*sim.rho_r.value/144 804 | plt.legend(loc='best', fontsize = 12) 805 | plt.xlabel('Position (in)', fontsize = 12) 806 | plt.ylabel('Load (lbf)', fontsize = 12) 807 | plt.tight_layout() 808 | plt.savefig('fig_14.eps', dpi = 1200, Transparent = True) 809 | plt.show() 810 | 811 | # figure 15 812 | 813 | # plot pump dynagraph where it works 814 | plt.figure() 815 | plt.rcParams['xtick.labelsize'] = 12 816 | plt.rcParams['ytick.labelsize'] = 12 817 | plt.plot((res['us'][npx-1][-600:]-np.min(res['us'][npx-1][-600:]))*12,-res['fs'][npx-1][-600:], label = 'Pump Dynagraph') 818 | plt.xlabel('Position (in)', fontsize = 12) 819 | plt.ylabel('Load (lbf)', fontsize = 12) 820 | plt.legend(loc='best', fontsize = 12) 821 | plt.tight_layout() 822 | plt.savefig('fig_15.eps', dpi = 1200, Transparent = True) 823 | plt.show() 824 | 825 | # figure 16 826 | 827 | # plot pump position vs. time 828 | plt.figure() 829 | plt.rcParams['xtick.labelsize'] = 12 830 | plt.rcParams['ytick.labelsize'] = 12 831 | plt.plot(res['ts'],res['us'][-1]-np.min(res['us'][-1]), label = 'Pump Position (ft)') 832 | # the minus np.min(res['us'][-1] is used to put the minimum at zero 833 | plt.plot(res['ts'],res['vs'][-1], label ='Pump Velocity (ft/s)') 834 | plt.plot(res['ts'],np.array(res['ys'])*10, label = 'Sgn(V)') 835 | plt.legend(loc='best', fontsize = 12) 836 | plt.xlabel('Time (seconds)', fontsize = 12) 837 | plt.tight_layout() 838 | plt.savefig('fig_16.eps', dpi = 1200, Transparent = True) 839 | plt.show() 840 | 841 | # figure 17 842 | 843 | # time for pump dynagraph 844 | plt.figure() 845 | plt.rcParams['xtick.labelsize'] = 12 846 | plt.rcParams['ytick.labelsize'] = 12 847 | plt.plot(res['ts'],-res['fs'][-1], label = 'Pump Load (lbf)') 848 | plt.legend(loc='best', fontsize = 12) 849 | plt.xlabel('Time (seconds)', fontsize = 12) 850 | plt.ylabel('Load (lbf)', fontsize = 12) 851 | plt.legend(loc='best', fontsize = 12) 852 | plt.tight_layout() 853 | plt.savefig('fig_17.eps', dpi = 1200, Transparent = True) 854 | plt.show() 855 | 856 | # figure 18 857 | 858 | # time for surface dynagraph 859 | plt.figure() 860 | plt.rcParams['xtick.labelsize'] = 12 861 | plt.rcParams['ytick.labelsize'] = 12 862 | plt.plot(res['ts'],-res['fs'][0], label = 'Pump Load (lbf)') 863 | plt.legend(loc='best', fontsize = 12) 864 | plt.xlabel('Time (seconds)', fontsize = 12) 865 | plt.ylabel('Load (lbf)', fontsize = 12) 866 | plt.legend(loc='best', fontsize = 12) 867 | plt.tight_layout() 868 | plt.savefig('fig_18.eps', dpi = 1200, Transparent = True) 869 | plt.show() 870 | -------------------------------------------------------------------------------- /MHE_MPC_reduced.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Apr 6 13:20:42 2018 4 | 5 | @author: Brandon 6 | """ 7 | 8 | #from __future__ import division # compatibility with python 2.7 9 | from gekko import GEKKO 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | import time 13 | 14 | #%% 15 | #Timespace for MPC and MHE########################### 16 | tf =1.0 # sec, length of simulation 17 | npt = 11 # number of time discretizations 18 | nit = 1 # number of itterations to solve 19 | ti = np.linspace(0,tf,npt) # times for plotting 20 | 21 | #Simulation Timespace############################### (Added) 22 | npt_sim = 21 23 | tf_sim =1.0 24 | 25 | # Define time space for MPC 26 | tf_mpc = 1.0 27 | npt_mpc = npt*tf_mpc 28 | 29 | 30 | # Define Rod Discretizations 31 | TVD_d = 4800 # ft, lenth of rod 32 | npx =10 # number of rod discretizations 33 | npx_m = 5 34 | 35 | 36 | 37 | dx = TVD_d/(npx-1) # #ft lenth of rod discretizations 38 | dx_m = TVD_d/(npx_m-1) 39 | xi = np.linspace(0,TVD_d,npx) # possitions allong rod (for plotting) 40 | xi_m= np.linspace(0,TVD_d,npx_m) 41 | #Set Points 42 | SPM_in = np.ones(npt)*10 43 | 44 | 45 | #BuildModel#################################################################### 46 | mpc = GEKKO() 47 | sim = GEKKO() 48 | mhe = GEKKO() 49 | 50 | 51 | #Horizon Window 52 | mhe.time = np.linspace(0,tf,npt) 53 | time_mpc = np.linspace(0,tf,npt) 54 | time_mpc = np.append(time_mpc, [1.1,1.4,2,2.5,3,3.5,4,4.5,5,5.5,6.5,7]) 55 | mpc.time = time_mpc#np.linspace(0,tf_mpc,npt_mpc) 56 | sim.time = np.linspace(0,tf_sim,npt_sim) 57 | 58 | 59 | ################################ 60 | # Conventional Rod Pump Unit Geometry 61 | # API geometry dimension values 62 | Ag=210.0 63 | Cg=120.3 64 | Ig=120.0 65 | Pg=148.5 66 | Hg=237.88 67 | Gg=86.88 68 | Rg=47.0 69 | 70 | #lengths from FIG. 1 - Beam Pumping Unit Shown as a Four-Bar Linkage 71 | L_1 = Rg 72 | L_2 = np.sqrt((Hg-Gg)**2.0+Ig**2.0) 73 | L_3 = Cg 74 | L_4 = Pg 75 | L_5 = Ag 76 | 77 | starting_height = 3/4 78 | 79 | #Setpoints 80 | level_height = 3 81 | SP = starting_height*TVD_d-level_height 82 | dSP = 0.1 83 | 84 | 85 | 86 | #Simulation######################################## 87 | 88 | for m in [sim,mpc,mhe]: 89 | #Constants 90 | m.API = m.Const(value = 45) #API gravity of fluid, unitless 91 | m.c = m.Const(value = 0.000013) #Compressibility, psi^-1 92 | m.k = m.Const(value = 15) #Permeability, md 93 | m.Bo = m.Const(value = 1.2) #FVF, rb/STB 94 | m.A_d = m.FV(value = 2, ub = 8, lb = 1) #Drainage Area, Acres 95 | m.sw = m.Const(value = 0.2) #Water Saturation 96 | m.porosity = m.FV(value = 0.08, ub = .12, lb = 0.07) #Porosity, unitless 97 | m.gamma_E = m.Const(value = 1.78) #Euler Constant 98 | m.C_a = m.Const(value = 31.6) #Drainage Area Shape Factor (Circular) 99 | m.rw = m.Const(value = 0.328) #Welbore radius, ft 100 | m.S = m.FV(value = 0, ub = 10, lb = -5) #unitless 101 | m.u_visc = m.Const(value = 1.5) # Viscosity, cp 102 | m.h_pz = m.Const(value = 8) #pay zone thickness, ft 103 | m.D_t = m.Const(value = 2.5) # tubing diameter, in 104 | m.St_length = m.Const(value = 85) # rod pump stroke length, in 105 | 106 | m.g = m.Const(value = 32.2) # acceleration due to gravity, ft/s^3 107 | m.g_conv= m.Const(value = 32.2) # lbf conversion , lb-ft/s^2-lbf 108 | m.rho_r = m.Const(value = 490) # lbs/ft^3, density of rod steel 109 | m.rho_w = m.Const(value = 62.3 ) # lbs/ft^3, density of water at standard conditions 110 | m.a = m.Const(value =18996.06 ) # ft/s speed of sound in steel 111 | m.D_r = m.Const(value = 1.0) # in, diameter of rod string 112 | m.Ac = m.Const(value= m.D_r.value**2/4.0*np.pi) # in^2, cross sectional area of rod 113 | m.nu = m.Const(value = 0.01) # unitless, damping coefficient 114 | m.pi = m.Const(value=np.pi) 115 | m.E = m.Const(value = 32025000.0) # psi sucker rod modulus of elasticity 116 | m.alpha = m.Const(value = 0.0) # pump parameter, unitless 117 | m.beta = m.Const(value = 1.0) # pump parameter, unitless 118 | 119 | m.L_1 = m.Const(value =L_1) # unit geometry 120 | m.L_2 = m.Const(value =L_2) # unit geometry 121 | m.L_3 = m.Const(value =L_3) # unit geometry 122 | m.L_4 = m.Const(value =L_4) # unit geometry 123 | m.L_5 = m.Const(value =L_5) # unit geometry 124 | 125 | if m == sim: 126 | m.dx = m.Const(value = dx) # ft delta x 127 | else: 128 | m.dx = m.Const(value = dx_m) 129 | 130 | #Prime Mover Constants (Torque Balance) 131 | m.tau_p = m.Const(value = 3) #tau 132 | m.k_gain = m.Const(value = 1) #one to one ratio between torque and SPM 133 | 134 | ##Economic 135 | m.Weight_lb_ft = m.Const(value = m.rho_r.value*m.Ac.value*m.g.value/m.g_conv/144) #Weight of rod string, lbf/ft 136 | m.Capex = m.Const(value = 200000) #Cost of Pumping Rod Unit,$? 137 | m.P_o = m.Const(value = 50) #Price of Oil, $/STB 138 | m.r = m.Const(value= .12/365) #Daily Discount Rate, % 139 | m.P_th = m.Const(value = 100) #tubing head pressure, psi 140 | m.TVD = m.Const(value = 4800) #true vertical depth, ft 141 | m.E_cost = m.Const(value = 0.13/3600) #Cost of Electricity, cents/Kws 142 | 143 | #Calculated Constants #DO NOT MODIFY# 144 | m.Wr = m.Const(value = m.TVD.value*m.Weight_lb_ft.value) #Weight of entire rod string, lbm 145 | m.D_a = m.Const(value = 2*12*m.rw.value) #Annulus Diameter, in 146 | m.gamma = m.Const(141.5/(m.API.value+131.5)) #Specific gravity of Fluid 147 | m.P_startpump = m.Const(value = 0.433*m.gamma.value*m.TVD.value) #Average Reservoir Pressure at Pump start up 148 | m.Pi = m.Const(value = .433*m.TVD.value) #Initial Reservoir Pressure, psi 149 | 150 | m.A_t = m.Const((np.pi/4)*m.D_t.value**2) #Cross sectional Area of tubing, in^2 151 | m.A_a = m.Const((np.pi/4)*m.D_a.value**2) #Cross Sectional Area of Annulus, in^2 152 | m.Wf = m.Const(value = m.TVD.value*m.rho_w.value*m.gamma.value*m.g.value/m.g_conv.value*(m.A_t.value-m.Ac.value)/144) # lbf, weight of fluid in tubing 153 | 154 | #MV's 155 | m.SPM_in = m.MV(value = 15, lb = 5, ub = 15) #Rod Pump Pumping Speed/Torque, spm 156 | 157 | #Variables 158 | m.V_i= m.Var(value = 7758*m.A_d.value*m.h_pz.value*m.porosity.value*(1-m.sw.value)/m.Bo.value) #OOIP, stb 159 | m.Vp = m.Var(value = m.V_i.value*(np.exp(m.c.value*(m.Pi.value-m.P_startpump.value))-1)) #initial volume produced prior stb 160 | 161 | if m == sim or m == mpc: 162 | m.h = m.CV(value = 1.0*m.TVD.value*starting_height) 163 | else: 164 | m.h_mv = m.MV(value = 1.0*m.TVD.value*starting_height, lb = 0, ub = 4800) 165 | m.h = m.Var(value = 1.0*m.TVD.value*starting_height, lb = 0, ub = 4800) # Height, ft 166 | m.Equation(m.h_mv == m.h) 167 | m.NPV = m.Var(value = -1.0*m.Capex.value) #Net Present Value, $ 168 | m.y = m.Var( lb = -1, ub = 1) # SIGN(x) 169 | m.sa = m.Var(value = 0, lb = 0) # slack variable a 170 | m.sb = m.Var(value = 0, lb = 0) # slack variable b 171 | m.tsi = m.Var(value = 0.0) # mulation time 172 | m.SPM = m.Var(value = 15) #SPM, strokes/min 173 | #omega = m.Var(value = 0) 174 | m.theta = m.Var(value = 0) # rad i.e sec^-1 crank angle of surface unit 175 | if m == sim: 176 | m.u = [m.SV(value = 9.22) for i in range(npx)] # relative position of each rod segment 177 | m.v = [m.Var(value = 0.0) for i in range(npx)] # velocity of reach rod segment 178 | m.f = [m.SV(value = 0.0) for i in range (npx)] # load at each rod segment 179 | 180 | else: 181 | m.u = [m.SV(value = 9.22) for i in range(npx_m)] # relative position of each rod segment 182 | m.v = [m.Var(value = 0.0) for i in range(npx_m)] # velocity of reach rod segment 183 | m.f = [m.SV(value = 0.0) for i in range (npx_m)] # load at each rod segment 184 | 185 | m.P = m.Var(value = 1e-6) # unitless, load at the pump 186 | 187 | 188 | ## State Variables 189 | m.P_res = m.Var(value = m.P_startpump.value*1.0) #Current Reservoir Pressure , psi 190 | m.P_wf = m.Var(value = 0.433*m.gamma*m.h.value) #Bottomhole Flowing Pressure, psi 191 | m.q_in = m.Var(value = (1/86400)*m.k.value*m.h_pz.value*(m.P_res.value-m.P_wf.value)/(141.2*m.Bo.value*m.u_visc.value*((1/2)*np.log(4*m.A_d.value/(m.gamma_E.value*m.C_a.value*m.rw.value**2)) + m.S.value))) #IPR-VLP Flow rate, STB/s 192 | m.q_out = m.Var(value = 0) # Outgoing Flow Rate, STB/s 193 | m.t = m.Var(value = 0) #Time, days 194 | m.W_rod = m.Var(value = (1.0962)*m.q_out.value*(m.P_th.value-m.P_wf.value + .433*m.gamma.value*m.TVD.value) + (4.7053e-7)*m.Wr.value*m.St_length.value*m.SPM.value) #Work supplied by electric Motor, KW 195 | 196 | #Intermediates 197 | m.hs = m.Intermediate(m.sqrt(L_1**2 +L_2**2 + 2 *L_1 *L_2 *m.cos(m.theta))) 198 | 199 | #Equations 200 | ##AlgebraicEqns 201 | m.Equation(m.V_i == 7758*m.A_d*m.h_pz*m.porosity*(1-m.sw)/m.Bo) 202 | m.Equation(m.P_wf == 0.433*m.gamma*m.h) 203 | m.Equation(m.P_res == m.Pi-(1/m.c)*m.log((m.Vp/m.V_i)+1)) 204 | m.Equation(m.q_in == (1/86400)*m.k*m.h_pz*(m.P_res-m.P_wf)/(141.2*m.Bo*m.u_visc*((1/2)*m.log(4*m.A_d/(m.gamma_E*m.C_a*m.rw**2)) + m.S))) #STB/s 205 | m.Equation(m.W_rod == (1.0962)*m.q_out*(m.P_th-m.P_wf + .433*m.gamma*m.TVD) + (4.7053e-7)*m.Wr*m.St_length*m.SPM) 206 | 207 | #Prime Mover Equations- Torque Balance and Kinematic Eqns 208 | m.Equation(m.SPM.dt() == -(1/m.tau_p)*m.SPM + (m.k_gain/m.tau_p)*m.SPM_in) 209 | m.Equation((2*m.pi/60)*m.SPM == m.theta.dt()) 210 | 211 | m.Equation(m.u[0] == (1/12)*L_5*(m.asin(L_1*m.sin(m.theta)/m.hs)+m.acos((m.hs**2+L_3**2-L_4**2)/(2*L_3*m.hs)))) # position of polished rod, inches 212 | 213 | if m == sim: 214 | [m.Equation(m.v[i+1].dt()== m.a**2 * (m.u[i+2] - 2.0*m.u[i+1] + m.u[i])/m.dx**2 - m.pi*m.a*m.nu/(2.0*m.TVD)*m.v[i+1] - (1-m.rho_w*m.gamma/m.rho_r)*m.g) for i in range(npx-2) ]# wave equation 215 | else: 216 | [m.Equation(m.v[i+1].dt()== m.a**2 * (m.u[i+2] - 2.0*m.u[i+1] + m.u[i])/m.dx**2 - m.pi*m.a*m.nu/(2.0*m.TVD)*m.v[i+1] - (1-m.rho_w*m.gamma/m.rho_r)*m.g) for i in range(npx_m-2) ]# wave equation 217 | 218 | m.Equation(m.q_out == m.A_t * m.u[-1].dt()*12/231/42 * (1+m.y)/2) # rate of fluid production, barrels/ 219 | 220 | # Equations for calculating rod loading 221 | # Load at surface 222 | m.Equation(m.f[0] == m.E*m.Ac*1/2/m.dx *(-m.u[2] + 4*m.u[1] -3*m.u[0])) 223 | # Load at pump 224 | 225 | if m == sim: 226 | m.Equation(m.f[npx-1] == m.E*m.Ac* m.P) 227 | # load at intermediate points 228 | [m.Equation(m.f[1+i] == m.E*m.Ac*1/2.0/dx*(m.u[i+2] - m.u[i])) for i in range(npx-2)] 229 | # pump boundary 230 | m.Equation( m.u[npx-1]*m.alpha + (m.u[npx-1] - m.u[npx-2])/dx == m.P) 231 | #add in signum for lifting and lowering conditions 232 | else: 233 | m.Equation(m.f[npx_m-1] == m.E*m.Ac* m.P) 234 | # load at intermediate points 235 | [m.Equation(m.f[1+i] == m.E*m.Ac*1/2.0/dx*(m.u[i+2] - m.u[i])) for i in range(npx_m-2)] 236 | # pump boundary 237 | m.Equation( m.u[npx_m-1]*m.alpha + (m.u[npx_m-1] - m.u[npx_m-2])/dx == m.P) 238 | #add in signum for lifting and lowering conditions 239 | m.Equation(m.v[-1] == m.sb - m.sa ) 240 | m.Equation(m.P == -((m.Wf- (m.A_t - m.Ac)*m.P_wf)/m.E/m.Ac) * (1 + m.y)/2 ) # -P_wf*A_t 241 | 242 | 243 | ##DifferentialEans 244 | m.Equation(m.t.dt() == 1) 245 | m.Equation(m.Vp.dt() == m.q_in) 246 | m.Equation(m.NPV.dt() == (m.P_o*m.q_out-m.E_cost*m.W_rod)*m.exp(-m.r*m.t)) 247 | m.Equation(m.h.dt() == (1617/2)*(m.q_in - m.q_out)/(m.A_a -m.A_t)) 248 | m.Equation(m.tsi.dt()==1.0) # create time variable 249 | 250 | if m == sim: 251 | [m.Equation(m.u[i].dt()==m.v[i]) for i in range(npx)] # velocity of rod string 252 | else: 253 | [m.Equation(m.u[i].dt()==m.v[i]) for i in range(npx_m)] # velocity of rod string 254 | 255 | # Set Objectives ################################################## 256 | if m == sim: 257 | m.Obj((m.sa*(1+m.y) + m.sb*(1-m.y))) # objective function to make signum work. 258 | elif m== mhe: 259 | m.Obj((m.sa*(1+m.y) + m.sb*(1-m.y))) # objective function to make signum work. 260 | m.Equation((m.sa*(1+m.y) + m.sb*(1-m.y))<=1e-8) 261 | else: 262 | m.Obj((m.sa*(1+m.y) + m.sb*(1-m.y))) # objective function to make signum work. 263 | #m.Equation((m.sa*(1+m.y) + m.sb*(1-m.y))<=1e-3) 264 | #SetGlobalOptions(Simulation)############################################################## 265 | sim.options.IMODE = 5 # 4 = Dynamic Simulation (Seqential) 266 | sim.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 267 | sim.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 268 | sim.options.time_shift = npt_sim-1 # time shift forward for multiple simulations 269 | sim.options.MAX_ITER = 450 270 | 271 | #SetLocalOptions############################################################### 272 | #N/A 273 | sim.SPM_in.FSTATUS = 1 # accept measurments 274 | sim.SPM_in.STATUS = 0 # don't let optimizer change (simulation) 275 | 276 | #MHE########################################################################### 277 | #Parameters (Holds Measured values from MHE) 278 | fm = mhe.Param(value = sim.f[0].value) 279 | 280 | #SetGlobalOptions(MHE)########################################################## 281 | mhe.options.IMODE = 5 # 4 = Dynamic Simulation (Seqential) 282 | mhe.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 283 | mhe.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 284 | mhe.options.time_shift = npt-1 # time shift forward for multiple simulations 285 | mhe.options.MAX_ITER = 1000 286 | mhe.Obj((mhe.f[0] - fm)**2) 287 | 288 | #SetLocalOptions (MHE)############################################################### 289 | ##FV #Variable to estimate 290 | mhe.h_mv.FSTATUS = 0 291 | mhe.h_mv.STATUS = 1 292 | #mhe.h.DMAX = 0.5 293 | mhe.h_mv.DMAX = 0.5#0.05 294 | mhe.h_mv.DCOST = 0.01 295 | 296 | #MV 297 | mhe.SPM_in.FSTATUS = 1 298 | mhe.SPM_in.STATUS = 0 299 | 300 | #SetGlobalOptions(MPC)########################################################## 301 | mpc.options.IMODE = 6 # 4 = Dynamic Simulation (Seqential) 302 | mpc.options.NODES = 2 # 3 = 3 Nodes, 2 = No collocation nodes 303 | mpc.options.SOLVER = 3 # 1 =APOPT, 3 = IPOPT 304 | mpc.options.time_shift = npt-1 # time shift forward for multiple simulations 305 | mpc.options.MAX_ITER = 450 306 | mpc.options.CV_TYPE = 1 307 | 308 | #SetLocalOptions############################################################### 309 | ##FV 310 | mpc.SPM_in.STATUS = 1 311 | mpc.SPM_in.FSTATUS = 0 # cound be meausured, but we are setting it 312 | mpc.SPM_in.DMAX = .05 # maximum change in SPM per cycle 313 | #mpc.SPM_in.DCOST = .01 314 | 315 | # CV's 316 | mpc.h.STATUS = 1 # control h 317 | mpc.h.FSTATUS = 1 # accept measurements 318 | mpc.h.TR_INIT = 2 # create trajectory to setpoint 319 | mpc.h.TAU = .1 # time constant of trajectory to h set point 320 | 321 | #Solve######################################################################### 322 | #%% 323 | # Solve the simulation in a loop to simulate a longer horizon 324 | loops = 180 # number of steps forward in time 325 | res = {} 326 | solve_stat = np.zeros(loops) 327 | t_cycle = 0.0 328 | #Initialize Storage Values 329 | sim_ts = np.ones(npt_sim)*sim.tsi.value # simulation time storage 330 | sim_hstor = np.ones(npt_sim)*sim.h.value # height of fluid in annulus storage 331 | sim_q_ins= np.ones(npt_sim)*sim.q_in.value # reservoir influx storage 332 | sim_q_outs = np.ones(npt_sim)*sim.q_out.value # production rate storage 333 | sim_P_ress = np.ones(npt_sim)*sim.P_res.value # reservoir pressure storage 334 | sim_Vps = np.ones(npt_sim)*sim.Vp.value # cumulative volume produced storage 335 | sim_NPVs = np.ones(npt_sim)*sim.NPV.value # NPV storage 336 | sim_W_rods = np.ones(npt_sim)*sim.W_rod.value # work of rod (work to lift fluid) storage 337 | sim_SPMs = np.ones(npt_sim)*sim.SPM_in.value # Strokes per minute/ Torque storage Set Points 338 | sim_SPMr = np.ones(npt_sim)*sim.SPM.value #SPM storage 339 | sim_thetas = np.ones(npt_sim)*sim.theta.value#Theta storage 340 | sim_P_wfs = np.ones(npt_sim)*sim.P_wf.value # bottom hole pressure storage 341 | sim_ys = np.ones(npt_sim)*sim.y.value # sign of du/dt storage 342 | 343 | 344 | #MHE Storage 345 | mpc_ts = np.zeros(0) # simulation time storage 346 | mhe_us = [np.array(mpc.u[i].value) for i in range(npx_m)] # u relative position storage 347 | mhe_vs = [np.array(mpc.v[i].value) for i in range(npx_m)] 348 | mhe_fs = [np.array(mpc.f[i].value) for i in range(npx_m)] # dynamic load storage 349 | mpc_hstor = np.zeros(0)# height of fluid in annulus storage 350 | mpc_q_ins= np.zeros(0) # reservoir influx storage 351 | mpc_q_outs = np.zeros(0) # production rate storage 352 | mpc_P_ress = np.zeros(0) # reservoir pressure storage 353 | mpc_Vps = np.zeros(0) # cumulative volume produced storage 354 | mpc_NPVs = np.zeros(0) # NPV storage 355 | mpc_W_rods =np.zeros(0) # work of rod (work to lift fluid) storage 356 | mpc_SPMs = np.zeros(0) # Strokes per minute/ Torque storage Set Points 357 | mpc_SPMr = np.zeros(0) #SPM storage 358 | mpc_thetas = np.zeros(0)#Theta storage 359 | mpc_P_wfs = np.zeros(0) # bottom hole pressure storage 360 | mpc_ys = np.zeros(0) # sign of du/dt storage 361 | mpc_Skins = np.zeros(0) #Skin storage 362 | mpc_porosity = np.zeros(0) 363 | mpc_A_d = np.zeros(0) 364 | mhe_Skins = np.zeros(0) #Skin storage 365 | mhe_hstor = np.zeros(0)# height of fluid in annulus storage 366 | mpc_Skins = np.zeros(0) 367 | mpc_Skinss = np.zeros(0) 368 | mpc_h = np.zeros(0) 369 | mpc_hss = np.zeros(0) 370 | ############################################################### 371 | 372 | 373 | for i in range(loops): 374 | 375 | # simulate system for 1 second 376 | sim.solve() # (remote = False) for local solve 377 | if i == 0: 378 | # Create and store results 379 | sim_ts = np.array(sim.tsi.value) # simulation time storage 380 | sim_us = [np.array(sim.u[i].value) for i in range(npx)] # u relative position storage 381 | sim_vs = [np.array(sim.v[i].value) for i in range(npx)] 382 | sim_fs = [np.array(sim.f[i].value) for i in range(npx)] # dynamic load storage 383 | sim_hstor = np.array(sim.h.value) # height of fluid in annulus storage 384 | sim_q_ins= np.array(sim.q_in.value) # reservoir influx storage 385 | sim_q_outs = np.array(sim.q_out.value) # production rate storage 386 | sim_P_ress = np.array(sim.P_res.value) # reservoir pressure storage 387 | sim_Vps = np.array(sim.Vp.value) # cumulative volume produced storage 388 | sim_NPVs = np.array(sim.NPV.value) # NPV storage 389 | sim_W_rods = np.array(sim.W_rod.value) # work of rod (work to lift fluid) storage 390 | sim_SPMs = np.array(sim.SPM_in.value) # Strokes per minute/ Torque storage Set Points 391 | sim_SPMr = np.array(sim.SPM.value) #SPM storage 392 | sim_thetas = np.array(sim.theta.value)#Theta storage 393 | sim_P_wfs = np.array(sim.P_wf.value) # bottom hole pressure storage 394 | sim_ys = np.array(sim.y.value) # sign of du/dt storage 395 | 396 | elif i>0: 397 | sim_ts = np.append(sim_ts,sim.tsi.value) # simulation time storage 398 | sim_us = [np.append(sim_us[i],sim.u[i].value) for i in range(npx)] # u relative position storage 399 | sim_vs = [np.append(sim_vs[i],sim.v[i].value) for i in range(npx)] 400 | sim_fs = [np.append(sim_fs[i],sim.f[i].value) for i in range(npx)] # dynamic load storage 401 | sim_hstor = np.append(sim_hstor,sim.h.value) # height of fluid in annulus storage 402 | sim_q_ins= np.append(sim_q_ins,sim.q_in.value) # reservoir influx storage 403 | sim_q_outs = np.append(sim_q_outs,sim.q_out.value) # production rate storage 404 | sim_P_ress = np.append(sim_P_ress,sim.P_res.value) # reservoir pressure storage 405 | sim_Vps = np.append(sim_Vps,sim.Vp.value) # cumulative volume produced storage 406 | sim_NPVs = np.append(sim_NPVs,sim.NPV.value) # NPV storage 407 | sim_W_rods = np.append(sim_W_rods,sim.W_rod.value) # work of rod (work to lift fluid) storage 408 | sim_SPMs = np.append(sim_SPMs,sim.SPM_in.value) # Strokes per minute storage 409 | sim_SPMr = np.append(sim_SPMr,sim.SPM.value) #Strokes per minute storage 410 | sim_thetas = np.append(sim_thetas,sim.theta.value) 411 | sim_P_wfs = np.append(sim_P_wfs,sim.P_wf.value) # bottom hole pressure storage 412 | sim_ys = np.append(sim_ys,sim.y.value) # sign of du/dt storage 413 | solve_stat[i] = t_cycle 414 | ##MHE################################################################## 415 | #Insert Measurements to MHE 416 | fm.value = np.array(sim.f[0].value)[0:npt_sim:2] #(Modified) 417 | 418 | mhe.SPM_in.value = np.array(sim.SPM_in.value)[0:npt_sim:2] #(Modified) 419 | 420 | #Solve 421 | t_start = time.time() 422 | mhe.solve() # (remote = False) for local solveremote = False) 423 | 424 | #Pass values to MPC 425 | mpc.h.MEAS = mhe.h_mv.NEWVAL #sim.h.value[-1]# 426 | 427 | #Store new values for plotting 428 | mhe_hstor = np.append(mhe_hstor,mhe.h.value) 429 | 430 | ##MPC ##################################################################### 431 | mpc.h.SP = SP # ft 432 | mpc.h.SPHI = SP + dSP# + 1 433 | mpc.h.SPLO = SP - dSP#- 434 | mpc.h.TAU = .1 435 | mpc.solve() # (remote=False) for local solve 436 | t_end = time.time() 437 | t_cycle = t_end - t_start 438 | 439 | #takes first npt control moves in MPC horizon and extends it to npt_sim control moves to pass to simulation 440 | mpcSpmOutput = np.array([mpc.SPM_in.value[0:npt]]) 441 | mpcSpmOutput = np.repeat(mpcSpmOutput,2)[1:] 442 | 443 | if(mpc.options.APPSTATUS ==1): 444 | # store spm 445 | sim.SPM_in.value = mpcSpmOutput 446 | #sim.SPM_in.value = mpc.SPM_in.value[0:npt_sim] 447 | 448 | else: 449 | #sim.SPM_in.value = 450 | exit 451 | mpc.h.TR_INIT = 1 452 | if i ==0: 453 | mpc_hs = np.array(mpc.h.value[0:npt]) 454 | elif i>0: 455 | mpc_hs = np.append(mpc_hs, mpc.h.value[0:npt]) 456 | ####################################################################### 457 | 458 | 459 | # Plotting 460 | plt.clf() 461 | ax=plt.subplot(411) 462 | ax.grid() 463 | plt.plot(sim_ts[0:i*npt_sim],sim_SPMs[0:i*npt_sim],'ro',label='SPM Set Point') 464 | plt.plot(sim_ts[0:i*npt_sim],sim_SPMr[0:i*npt_sim],'bo',label='SPM') 465 | plt.ylabel('Strokes per Minute') 466 | plt.legend(loc=2) 467 | ax=plt.subplot(412) 468 | ax.grid() 469 | plt.plot(sim_ts[0:i*npt_sim],sim_hstor[0:i*npt_sim],'k-',label= 'height') 470 | plt.plot(sim_ts[0:i*npt_sim], np.ones(i*npt_sim)*SP, label = 'height SP') 471 | plt.plot(sim_ts[0:i*npt_sim], np.ones(i*npt_sim)*mhe.h_mv.NEWVAL, label = 'height MHE Prediction') 472 | #plt.plot(ts[0:i*npt], mpc_hs[0:npt*i], label = 'mpc height') 473 | plt.ylabel('Annular Fluid Height') 474 | plt.legend(loc='best') 475 | ax = plt.subplot(413) 476 | ax.grid() 477 | plt.plot(sim_ts[0:i*npt_sim], sim_q_outs[0:i*npt_sim], label = 'q_out') 478 | plt.plot(sim_ts[0:i*npt_sim], sim_q_ins[0:i*npt_sim], label = 'q_in') 479 | plt.legend() 480 | plt.ylabel('Flow Rate, STB/s') 481 | plt.xlabel('Time (sec)') 482 | #plt.draw() 483 | #============================================================================== 484 | ax = plt.subplot(414) 485 | ax.grid() 486 | plt.plot(np.linspace(0,i,npt*i), mhe_hstor[0:i*npt], label = 'mhe_height') 487 | plt.plot(sim_ts[0:i*npt_sim], sim_hstor[0:i*npt_sim], label = 'Actual height') 488 | plt.legend() 489 | plt.ylabel('Annular Fluid Height') 490 | plt.xlabel('Time (sec)') 491 | plt.draw() 492 | #============================================================================== 493 | plt.pause(0.02) 494 | 495 | # 496 | #%% 497 | 498 | res['solve_stat'] = solve_stat 499 | res['ts' ] = sim_ts 500 | res['us' ] = sim_us 501 | res['vs' ] = sim_vs 502 | res['fs'] = sim_fs 503 | res['hstor' ] = sim_hstor 504 | res[ 'q_ins' ] = sim_q_ins 505 | res[ 'q_outs' ] = sim_q_outs 506 | res['P_ress' ] = sim_P_ress 507 | res['Vps' ] = sim_Vps 508 | res[ 'NPVs' ] = sim_NPVs 509 | res['W_rods' ] = sim_W_rods 510 | res['SPMs'] = sim_SPMs 511 | res['SPMr'] = sim_SPMr 512 | res['thetas'] = sim_thetas 513 | res['P_wfs'] = sim_P_wfs 514 | res[ 'ys'] = sim_ys 515 | res['h_SP'] = np.ones(loops*npt)*SP 516 | res['mhe_hstor']= mhe_hstor 517 | #%% 518 | np.save('MPC_MHE_Control_Results_10npt_5npx.npy', res) 519 | #%% 520 | # Load dictionary of results 521 | res = np.load('MPC_MHE_Control_Results_10npt_5npx.npy').item() 522 | 523 | #%% Plotting from dictionary 524 | 525 | 526 | plt.figure() 527 | plt.rcParams['xtick.labelsize'] = 12 528 | plt.rcParams['ytick.labelsize'] = 12 529 | ax=plt.subplot(311) 530 | ax.grid() 531 | plt.plot(res['ts'], res['SPMs'], 'r--', label=r'$T_{net}$ (ft-lb)')#, s = 4, c='b' ) # 'ro', for latex 532 | plt.plot(res['ts'], res['SPMr'],'b-', label=r'Actual')#, s = 4, c = 'r') #'bo', 533 | plt.ylabel('SPM', fontsize = 12) 534 | plt.legend(loc= 1,fontsize = 12) 535 | plt.ylim(5,16) 536 | plt.xlim(0,180) 537 | ax=plt.subplot(312) 538 | ax.grid() 539 | plt.plot(res['ts'], res['hstor'],'k-',label= 'Actual') 540 | plt.plot(res['ts'], np.ones(np.size(res['ts']))*(sim.TVD.value*3/4 -3), label = 'SP') # fix 541 | #plt.plot(ts[0:i*npt], mpc_hs[0:npt*i], label = 'mpc height') 542 | plt.ylabel('Fluid Level (ft)', fontsize = 12) 543 | plt.legend(loc=1,fontsize = 12) 544 | plt.xlim(0,180) 545 | ax = plt.subplot(313) 546 | ax.grid() 547 | plt.plot(res['ts'], res['q_outs'], label = r'$q_{out}$') 548 | plt.plot(res['ts'], res['q_ins'], label = r'$q_{in}$') 549 | plt.legend(loc = 1,fontsize = 12) 550 | plt.ylabel('Flow (STB/s)', fontsize = 12) 551 | plt.xlabel('Time (seconds)', fontsize = 12) 552 | plt.xlim(0,180) 553 | # 554 | ##ax = plt.subplot(414) 555 | ##ax.grid() 556 | ##plt.plot(res['ts'], res['mhe_hstor'], label = 'mhe height') 557 | ###plt.plot(sim_ts[0:i*npt], mpc_hs[0:i*npt], label = 'mpc height') 558 | ##plt.plot(res['ts'],res['hstor'],'k-',label= 'true height') 559 | ##ax.legend(loc='lower center', bbox_to_anchor=(.89, .1), ncol=1, fancybox=True, shadow=True, fontsize=12) 560 | ###plt.legend() 561 | ##plt.ylabel('Height', fontsize=12) 562 | ##plt.xlabel('Time (sec)',fontsize=12) 563 | ## 564 | # 565 | ##plt.draw() 566 | # 567 | # 568 | ##ax = plt.subplot(414) 569 | ##ax.grid() 570 | ##plt.plot(sim_ts[0:i*npt], mhe_hstor[0:i*npt], label = 'mhe height') 571 | ###plt.plot(sim_ts[0:i*npt], mpc_hs[0:i*npt], label = 'mpc height') 572 | ##plt.plot(sim_ts[0:i*npt],sim_hstor[0:i*npt],'k-',label= 'true height') 573 | ##ax.legend(loc='lower center', bbox_to_anchor=(.89, .1), ncol=1, fancybox=True, shadow=True, fontsize=16) 574 | ###plt.legend() 575 | ##plt.ylabel('Height', fontsize=22) 576 | ##plt.xlabel('Time (sec)',fontsize=22) 577 | ##plt.legend(fontsize = 12) 578 | ##plt.ylabel('Fluid Level (ft)', fontsize = 12) 579 | ##plt.xlabel('Time (seconds)', fontsize = 12) 580 | # 581 | ##plt.tight_layout() 582 | #plt.savefig('MPC_MHE_Control_10_Sec_Horizon.eps', transparent = True, dpi = 1200) 583 | #plt.show() 584 | # 585 | #%% 586 | # timing figure 587 | plt.rcParams['xtick.labelsize'] = 12 588 | plt.rcParams['ytick.labelsize'] = 12 589 | plt.figure() 590 | plt.plot(np.linspace(1,180,179), res['solve_stat'][1:],'r-',label='Solve Time') 591 | plt.plot(np.linspace(1,loops,loops-1), np.ones(loops-1)*np.average(res['solve_stat'][1:]),'b--', label = 'Average') 592 | plt.plot(np.linspace(1,loops,loops-1), np.ones(loops-1),'k:', label = 'Real Time') 593 | plt.xlabel('Control Cycle', fontsize = 12) 594 | plt.ylabel('Computation Time (seconds)', fontsize = 12) 595 | plt.legend(fontsize = 12) 596 | plt.ylim(0,10) 597 | plt.xlim(0,180) 598 | plt.savefig('MPC_MHE_Simulation_Timing.eps', dpi = 1200, transparent = True) 599 | plt.savefig('MPC_MHE_Simulation_Timing.png', dpi = 200, transparent = True) 600 | plt.show() 601 | 602 | print('Average Solve Time '+ str(np.average(res['solve_stat'][1:])) + '(s)') 603 | # 604 | # 605 | # 606 | # 607 | ##%% ### Plotting after having ran it 608 | #ax=plt.subplot(411) 609 | #ax.grid() 610 | #plt.plot(sim_ts[0:i*npt],sim_SPMs[0:i*npt],'ro',label='Motor Torque') 611 | #plt.plot(sim_ts[0:i*npt],sim_SPMr[0:i*npt],'bo',label='SPM') 612 | #plt.ylabel('SPM', fontsize=22) 613 | #ax.legend(loc='lower center', bbox_to_anchor=(.88, .13), ncol=1, fancybox=True, shadow=True, fontsize=16) 614 | ##plt.legend(loc=2) 615 | #ax=plt.subplot(412) 616 | #ax.grid() 617 | #plt.plot(sim_ts[0:i*npt],sim_hstor[0:i*npt],'k-',label= 'height') 618 | #plt.plot(sim_ts[0:i*npt], np.ones(i*npt)*(sim.TVD.value*3/4 -3), label = 'height SP') 619 | ##plt.plot(ts[0:i*npt], mpc_hs[0:npt*i], label = 'mpc height') 620 | #plt.ylabel('Height', fontsize=22) 621 | #ax.legend(loc='lower center', bbox_to_anchor=(.90, .13), ncol=1, fancybox=True, shadow=True, fontsize=16) 622 | ##plt.legend(loc='best') 623 | #ax = plt.subplot(413) 624 | #ax.grid() 625 | #plt.plot(sim_ts[0:i*npt], sim_q_outs[0:i*npt], label = r'$q_{out}$') 626 | #plt.plot(sim_ts[0:i*npt], sim_q_ins[0:i*npt], label = r'$q_{in}$') 627 | #ax.legend(loc='lower center', bbox_to_anchor=(.92, .13), ncol=1, fancybox=True, shadow=True, fontsize=16) 628 | ##plt.legend() 629 | #plt.ylabel('STB/s', fontsize=22) 630 | ##plt.xlabel('Time (sec)') 631 | #plt.draw() 632 | #ax = plt.subplot(414) 633 | #ax.grid() 634 | #plt.plot(sim_ts[0:i*npt], mhe_hstor[0:i*npt], label = 'mhe height') 635 | ##plt.plot(sim_ts[0:i*npt], mpc_hs[0:i*npt], label = 'mpc height') 636 | #plt.plot(sim_ts[0:i*npt],sim_hstor[0:i*npt],'k-',label= 'true height') 637 | #ax.legend(loc='lower center', bbox_to_anchor=(.89, .1), ncol=1, fancybox=True, shadow=True, fontsize=16) 638 | ##plt.legend() 639 | #plt.ylabel('Height', fontsize=22) 640 | #plt.xlabel('Time (sec)',fontsize=22) 641 | #plt.draw() 642 | #plt.pause(0.02) 643 | 644 | 645 | ############################################################### 646 | #============================================================================== 647 | # 648 | # for i in range(loops): 649 | # 650 | # 651 | # mhe.solve(remote = False) 652 | # mhe_ts = np.append(mhe_ts,mhe.tsi.value) # simulation time storage 653 | # mhe_us = [np.append(mhe_us[i],mhe.u[i].value) for i in range(npx)] # u relative position storage 654 | # mhe_vs = [np.append(mhe_vs[i],mhe.v[i].value) for i in range(npx)] 655 | # mhe_fs = [np.append(mhe_fs[i],mhe.f[i].value) for i in range(npx)] # dynamic load storage 656 | # mhe_hstor = np.append(mhe_hstor,mhe.h.value) # height of fluid in annulus storage 657 | # mhe_q_ins= np.append(mhe_q_ins,mhe.q_in.value) # reservoir influx storage 658 | # mhe_q_outs = np.append(mhe_q_outs,mhe.q_out.value) # production rate storage 659 | # mhe_P_ress = np.append(mhe_P_ress,mhe.P_res.value) # reservoir pressure storage 660 | # mhe_Vps = np.append(mhe_Vps,mhe.Vp.value) # cumulative volume produced storage 661 | # mhe_NPVs = np.append(mhe_NPVs,mhe.NPV.value) # NPV storage 662 | # mhe_W_rods = np.append(mhe_W_rods,mhe.W_rod.value) # work of rod (work to lift fluid) storage 663 | # mhe_SPMs = np.append(mhe_SPMs,mhe.SPM_in.value) # Strokes per minute storage 664 | # mhe_SPMr = np.append(mhe_SPMr,mhe.SPM.value) #Strokes per minute storage 665 | # mhe_thetas = np.append(mhe_thetas,mhe.theta.value) 666 | # mhe_P_wfs = np.append(mhe_P_wfs,mhe.P_wf.value) # bottom hole pressure storage 667 | # mhe_ys = np.append(mhe_ys,mhe.y.value) # sign of du/dt storage 668 | #============================================================================== 669 | 670 | 671 | #PlotResults################################################################### 672 | #============================================================================== 673 | # #%% 674 | # ## Figure 1 675 | # plt.figure(1, figsize = (6,4.5)) 676 | # plt.subplot(211) 677 | # plt.plot(mhe_ts, mhe_hstor, 'r--', label = 'height in annulus') 678 | # plt.ylabel('height, ft') 679 | # plt.legend() 680 | # 681 | # plt.subplot(212) 682 | # plt.plot(mhe_ts, mhe_q_ins, 'b--', label = r'$q_{in}$') 683 | # plt.plot(mhe_ts, mhe_q_outs, 'g--', label = r'$q_{out}$') 684 | # plt.ylabel('Flow Rate, STB/s') 685 | # plt.xlabel('time, sec') 686 | # plt.legend() 687 | # 688 | # plt.show() 689 | # 690 | # ##Figure 2 691 | # plt.figure(2, figsize = (6,4.5)) 692 | # plt.subplot(211) 693 | # plt.plot(mhe_ts, mhe_P_ress, 'k--', label = 'Reservoir Pressure') 694 | # #plt.plot(m.time, P_wf.value, 'r--', label = r'$P_{wf}$') 695 | # plt.ylabel('Pressure, psi') 696 | # plt.legend() 697 | # 698 | # plt.subplot(212) 699 | # plt.plot(mhe_ts, mhe_Vps, '--', label = 'Cumulative Volume Produced') 700 | # plt.ylabel('Volume, STB') 701 | # plt.xlabel('time, sec') 702 | # plt.legend() 703 | # plt.tight_layout() 704 | # 705 | # plt.show() 706 | # 707 | # ##Figure 3 708 | # plt.figure(3, figsize = (6,4.5)) 709 | # plt.plot(mhe_ts, mhe_NPVs/(1e6), 'g:', label = 'NPV') 710 | # plt.xlabel('time, sec') 711 | # plt.ylabel('NPV, $ Millions') 712 | # plt.legend() 713 | # 714 | # plt.show() 715 | # 716 | # #Figure 4 717 | # plt.figure(4, figsize = (6,4.5)) 718 | # plt.subplot(311) 719 | # plt.plot(mhe_ts,mhe_W_rods, 'b-', label = 'Work Supplied by Motor' ) 720 | # plt.ylabel('KiloWatts, KW') 721 | # 722 | # plt.subplot(312) 723 | # plt.plot(mhe_ts, mhe_SPMs, 'r-', label = 'Work Supplied by Motor' ) 724 | # plt.ylabel('SPM') 725 | # 726 | # plt.subplot(313) 727 | # plt.plot(mhe_ts, mhe_P_wfs, 'r--', label = r'$P_{wf}$') 728 | # plt.ylabel('FBHP, psi') 729 | # plt.xlabel('time, sec') 730 | # #plt.tight_layout() 731 | # 732 | # plt.show() 733 | # 734 | # ##Figure 5 -Doublet Test 735 | # plt.figure(5, figsize = (6,4.5)) 736 | # plt.subplot(211) 737 | # plt.plot(mhe_ts, mhe_hstor, 'r--', label = 'height in annulus') 738 | # plt.ylabel('height, ft') 739 | # plt.legend() 740 | # 741 | # plt.subplot(212) 742 | # plt.plot(mhe_ts, mhe_SPMs, 'b--', label = r'SPM') 743 | # plt.ylabel('strokes/min') 744 | # plt.xlabel('time, sec') 745 | # plt.legend() 746 | # 747 | # plt.show() 748 | # 749 | # # store results in to structure for 3-d plotting 750 | # for i in range(npx): 751 | # if i ==0: 752 | # mhe_ustor = np.array([mhe_us[i]]) 753 | # mhe_tstor = np.array([mhe_ts]) 754 | # else: 755 | # mhe_ustor = np.vstack([mhe_ustor,mhe_us[i]]) 756 | # mhe_tstor = np.vstack([mhe_tstor,mhe_ts]) 757 | # 758 | # for i in range(len(mhe_ts)): 759 | # if i == 0: 760 | # xstor = xi 761 | # else: 762 | # xstor = np.vstack([xstor,xi]) 763 | # x = xstor.T 764 | # t = mhe_tstor 765 | # mhe_ustor = np.array(mhe_ustor) 766 | # 767 | # fig = plt.figure() 768 | # ax = fig.add_subplot(1,1,1,projection='3d') 769 | # ax.set_xlabel('Distance x') 770 | # ax.set_ylabel('Time t') 771 | # p = ax.plot_wireframe(x,t,mhe_ustor,rstride=1,cstride=1) 772 | # fig.show() 773 | # 774 | # plt.figure() 775 | # [plt.scatter(mhe_ts,mhe_us[i], label ='u' +str(i)) for i in range(npx)] 776 | # #plt.plot(m.time,u[-1],'r--') 777 | # plt.legend() 778 | # plt.show() 779 | # 780 | # # Plot surface dynagraph () 781 | # plt.figure() 782 | # plt.plot((mhe_us[0]- np.min(mhe_us[0]))*12,-mhe_fs[0] + mhe.TVD.value*mhe.Ac.value*mhe.rho_r.value/144,label = 'Surface Dynagraph') # dynamic plus static load 783 | # plt.legend() 784 | # plt.xlabel('Position (in)') 785 | # plt.ylabel('Load (lbf)') 786 | # plt.show() 787 | # 788 | # # plot pump dynagraph 789 | # plt.figure() 790 | # plt.plot((mhe_us[npx-1]-np.min(mhe_us[npx-1]))*12,-mhe_fs[npx-1], label = 'Pump Dynagraph') 791 | # plt.xlabel('Position (in)') 792 | # plt.ylabel('Load (lbf)') 793 | # plt.legend() 794 | # plt.show() 795 | # 796 | # # plot pump position vs. time 797 | # plt.figure() 798 | # plt.plot(mhe_ts,mhe_us[-1], label = 'Pump Position') 799 | # plt.plot(mhe_ts,mhe_vs[-1], label ='Pump Velocity') 800 | # plt.plot(mhe_ts,np.array(mhe_ys)*10, label = 'Sign(V)') 801 | # plt.legend() 802 | # plt.show() 803 | # 804 | # plt.figure() 805 | # plt.plot(mhe_ts,-mhe_fs[-1], label = 'Pump Load (lbf)') 806 | # plt.legend() 807 | # plt.xlabel('Time (s)') 808 | # plt.ylabel('Load (lbf)') 809 | # plt.legend() 810 | # plt.tight_layout() 811 | # 812 | # plt.show() 813 | # 814 | # #Plot SPM Dynamics 815 | # plt.figure() 816 | # plt.plot(mhe_ts,mhe_SPMs) 817 | # plt.plot(mhe_ts,mhe_SPMr) 818 | # plt.ylabel('SPM') 819 | # plt.xlabel('time,s') 820 | # 821 | # plt.show() 822 | # 823 | # #Plot theta Dynamics (radians) 824 | # plt.figure() 825 | # plt.plot(mhe_ts,mhe_thetas) 826 | # plt.xlabel('time, s') 827 | # plt.ylabel('radians') 828 | # plt.show() 829 | #============================================================================== 830 | 831 | 832 | --------------------------------------------------------------------------------