I would like to use GEKKO to incrementally solve a dynamic model. I need to insert some parameters at different points in the simulation that are determined stochastically using an external model.
As a simple proof of concept, I tried adjusting the PID control example from the tutorial website to solve in 10 minute increments.
m = GEKKO()
tf = 40
timesteps = np.linspace(0,tf,2*tf+1)
step = np.zeros(2*tf+1)
step[3:40] = 2.0
step[40:] = 5.0
# Controller model
Kc = 15.0 # controller gain
tauI = 2.0 # controller reset time
tauD = 1.0 # derivative constant
OP_0 = m.Const(value=0.0) # OP bias
OP = m.Var(value=0.0) # controller output
PV = m.Var(value=0.0) # process variable
SP = m.Param(value=step) # set point
Intgl = m.Var(value=0.0) # integral of the error
err = m.Intermediate(SP-PV) # set point error
m.Equation(Intgl.dt()==err) # integral of the error
m.Equation(OP == OP_0 + Kc*err + (Kc/tauI)*Intgl - PV.dt())
# Process model
Kp = 0.5 # process gain
tauP = 10.0 # process time constant
m.Equation(tauP*PV.dt() + PV == Kp*OP)
setpoint_final = []
pv_final = []
output_final = []
for i in range(8):
SP.value = step[10*i:10*(i+1)]
m.time = timesteps[10*i:10*(i+1)]
m.options.IMODE=7
m.solve(disp=False)
setpoint_final.append(SP.value)
pv_final.append(PV.value)
output_final.append(OP.value)
plt.figure()
plt.subplot(2,1,1)
plt.plot(timesteps[:-1],np.concatenate(output_final),'b:',label='OP')
plt.ylabel('Output')
plt.legend()
plt.subplot(2,1,2)
plt.plot(timesteps[:-1],np.concatenate(setpoint_final),'k-',label='SP')
plt.plot(timesteps[:-1],np.concatenate(pv_final),'r--',label='PV')
plt.xlabel('Time (sec)')
plt.ylabel('Process')
plt.legend()
plt.show()
I should get something like this:
Instead it seems to reset at each interval:
Is there something I am missing or is this type of simulation not possible in GEKKO?
Try using m.options.TIME_SHIFT=10, m.options.IMODE=4, and change the way the values are stored.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO(remote=False)
tf = 40
timesteps = np.linspace(0,tf,2*tf+1)
step = np.zeros(2*tf+1)
step[3:40] = 2.0
step[40:] = 5.0
# Controller model
Kc = 15.0 # controller gain
tauI = 2.0 # controller reset time
tauD = 1.0 # derivative constant
OP_0 = m.Const(value=0.0) # OP bias
OP = m.Var(value=0.0) # controller output
PV = m.Var(value=0.0) # process variable
SP = m.Param(value=step) # set point
Intgl = m.Var(value=0.0) # integral of the error
err = m.Intermediate(SP-PV) # set point error
m.Equation(Intgl.dt()==err) # integral of the error
m.Equation(OP == OP_0 + Kc*err + (Kc/tauI)*Intgl - PV.dt())
# Process model
Kp = 0.5 # process gain
tauP = 10.0 # process time constant
m.Equation(tauP*PV.dt() + PV == Kp*OP)
setpoint_final = []
pv_final = []
output_final = []
m.options.TIME_SHIFT=10
m.time = timesteps[0:10]
for i in range(8):
SP.value = step[10*i:10*(i+1)]
m.options.IMODE=4
m.solve(disp=False)
setpoint_final = np.concatenate((setpoint_final,SP.value))
pv_final = np.concatenate((pv_final,PV.value))
output_final = np.concatenate((output_final,OP.value))
plt.figure()
plt.subplot(2,1,1)
plt.plot(timesteps[:-1],output_final,'b:',label='OP')
plt.ylabel('Output')
plt.legend()
plt.subplot(2,1,2)
plt.plot(timesteps[:-1],setpoint_final,'k-',label='SP')
plt.plot(timesteps[:-1],pv_final,'r--',label='PV')
plt.xlabel('Time (sec)')
plt.ylabel('Process')
plt.legend()
plt.show()
Related
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO()
m.options.SOLVER = 1
m.options.IMODE = 3
Num_car = 1
TOU = [64.9,64.9,64.9,64.9,64.9,64.9,64.9,64.9,152.6,239.8,239.8,152.6,239.8,239.8,239.8,239.8,152.6,152.6,152.6,152.6,152.6,152.6,152.6,64.9]
n=len(TOU)
p_i = m.Array(m.Var,(n,Num_car))
input = m.Array(m.Var, (n), value = 0.0, lb = 0.0, ub = 7.0, integer = True)
SOC_t = m.Array(m.Var,(n, Num_car))
for tt in range(0,n):
for i in range(0,Num_car):
SOC_t[tt,i].lower = 30
SOC_t[tt,i].upper = 70
SOC_t[0,0] = 30
eq_car_bat = np.zeros((n))
eq_car_bat = list(eq_car_bat)
for tt in range(0,n):
eq_car_bat[tt] = SOC_t[tt] + input[tt] == p_i[tt]
m.Equation(eq_car_bat)
SOC_Max = 90
SOC_Min = 30
sum_soc = sum(SOC_t[tt])
eq_total = np.zeros((n))
eq_total = list(eq_total)
eq_total = sum_soc == SOC_Max
m.Equation(eq_total)
for i in range(n):
m.Minimize(TOU[i]*p_i[i])
m.options.IMODE = 3
m.options.SOLVER = 1
m.solve(disp=True)
I want to find minimized charging costs when the EV charging.
My code is shown below but, I received the following error "x must be a python list of GEKKO parameters, variables, or expressions" that I don't know how to solve.
The intention appears to be to decide when to charge a battery over a 24 hour time period given a specific Time of Use (TOU). Here is a modified version of the script that charges (integer values 0-7) from 30 to 70 on the SOC. It has the capability to add additional vehicles but there is currently only one.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO()
m.options.SOLVER = 1
m.options.IMODE = 3
Num_car = 1
TOU = [64.9,64.9,64.9,64.9,64.9,64.9,64.9,64.9,152.6,239.8,
239.8,152.6,239.8,239.8,239.8,239.8,152.6,152.6,
152.6,152.6,152.6,152.6,152.6,64.9]
n=len(TOU)
inp = m.Array(m.Var, (n), value = 0.0,
lb = 0.0, ub = 7.0, integer = True)
SOC_Min = 30; SOC_Max = 90
# set bounds 30-90
SOC_t = m.Array(m.Var,(n, Num_car),lb=SOC_Min,ub=SOC_Max)
# set new bounds 30-70
for tt in range(0,n):
for j in range(Num_car):
SOC_t[tt,j].lower = 30
SOC_t[tt,j].upper = 70
for j in range(Num_car):
# initial SOC
m.Equation(SOC_t[0,j]==30) # initial charge at start
m.Equation(SOC_t[n-1,j]==70) # desired charge at end
for tt in range(1,n):
m.Equation(SOC_t[tt,j] == SOC_t[tt-1,j] + inp[tt])
for tt in range(n):
m.Minimize(TOU[tt]*inp[tt])
m.options.IMODE = 3
m.options.SOLVER = 1
m.solve(disp=True)
plt.figure(figsize=(8,5))
plt.subplot(3,1,1)
for j in range(Num_car):
p = np.empty(n)
for tt in range(n):
p[tt] = SOC_t[tt,j].value[0]
plt.plot(p,'r.-',label='vehicle '+str(j+1))
plt.legend(); plt.ylabel('SOC'); plt.grid()
plt.subplot(3,1,2)
p = np.empty(n)
for tt in range(n):
p[tt] = inp[tt].value[0]
plt.plot(p,'ko-',label='charge rate')
plt.legend(); plt.ylabel('charge'); plt.grid()
plt.subplot(3,1,3)
plt.plot(TOU,'bs-',label='electricity price')
plt.ylabel('price'); plt.grid()
plt.legend(); plt.xlabel('Time (hr)')
plt.tight_layout()
plt.savefig('soc_results.png',dpi=300)
plt.show()
This problem is related to some of the other problems in the Energy Benchmarks. Gekko has the capability to manage the time aspect of the problem when using IMODE=6 instead of indexing time explicitly. Using IMODE=3 (default) gives more control over the problem structure. IMODE=6 is better if there are differential equations.
I am starting to learn Gekko and I am testing optimal control problems. I am trying to solve the following optimal control problem with Gekko
The solution of this problem is (x_1(t) = (t-2)^2 - 2)
How to build the constraint x(0) + x(2) = 0?
My code gives me a wrong solution.
m = GEKKO(remote=False) # initialize gekko
nt = 101
m.time = np.linspace(0,2,nt)
#end_loc = nt-1
# Variables
x1 = m.CV(fixed_initial=False)
x2 = m.CV(fixed_initial=False)
x3 = m.Var(value=0)
#u = m.Var(value=0,lb=-2,ub=2)
u = m.MV(fixed_initial=False,lb=-2,ub=2)
u.STATUS = 1
p = np.zeros(nt) # mark final time point
p[-1] = 1.0
final = m.Param(value=p)
p1 = np.zeros(nt)
p1[0] = 1.0
p1[-1] = 1.0
infin = m.Param(value=p1)
# Equations
m.Equation(x1.dt()==x2)
m.Equation(x2.dt()==u)
m.Equation(x3.dt()==x1)
# Constraints
m.Equation(infin*x1==0)
m.Equation(final*x2==0)
m.Obj(x3*final) # Objective function
#m.fix(x2,pos=end_loc,val=0.0)
m.options.IMODE = 6 # optimal control mode
#m.solve(disp=True) # solve
m.solve(disp=False) # solve
plt.figure(1) # plot results
plt.plot(m.time,x1.value,'k-',label=r'$x_1$')
plt.plot(m.time,x2.value,'b-',label=r'$x_2$')
plt.plot(m.time,x3.value,'g-',label=r'$x_3$')
plt.plot(m.time,u.value,'r--',label=r'$u$')
plt.legend(loc='best')
plt.xlabel('Time')
plt.ylabel('Value')
plt.show()
plt.figure(1) # plot results
plt.plot(m.time,x1.value,'k-',label=r'$x_1$')
plt.plot(m.time,(m.time-2)**2-2,'g--',label=r'$\hat x_1$')
plt.legend(loc='best')
plt.xlabel('Time')
plt.ylabel('Value')
plt.show()
Use m.integral or m.vsum() to create a time weighted summation or vertical summation along the time direction. Here is a solution that replicates the exact solution.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO(remote=True) # initialize gekko
nt = 501
m.time = np.linspace(0,2,nt)
# insert a small time step at the beginning and end
# for final and initial condition equation x(1e-8)+x(2-1e-8)=0
# this doesn't change the numerical solution significantly
m.time = np.insert(m.time,1,1e-8)
m.time = np.insert(m.time,len(m.time)-1,2.0-1e-8)
nt += 2
# Variables
x1 = m.Var(fixed_initial=False,lb=-100,ub=100)
x2 = m.Var(fixed_initial=False)
u = m.MV(fixed_initial=False,lb=-2,ub=2)
u.STATUS = 1
p = np.zeros(nt) # mark final time point
p[-2] = 1.0
final = m.Param(value=p)
q = p.copy()
q[1] = 1.0
final_initial = m.Param(value=q)
xfi = m.Var()
m.Equation(xfi==final_initial*x1)
# Equations
m.Equation(x1.dt()==x2)
m.Equation(x2.dt()==u)
m.Equation(final*m.vsum(xfi)==0)
m.Equation(final*x2==0)
m.Minimize(m.integral(x1)*final) # Objective function
m.options.IMODE = 6 # optimal control mode
m.options.NODES = 2
m.solve(disp=True) # solve
plt.figure(1) # plot results
plt.subplot(2,1,1)
plt.plot(m.time,x1.value,'k-',label=r'$x_1$')
plt.plot(m.time,x2.value,'b-',label=r'$x_2$')
plt.plot(m.time,u.value,'--',color='orange',label=r'$u$')
plt.legend(loc='best')
plt.ylabel('Value')
plt.subplot(2,1,2)
plt.plot(m.time,x1.value,'k-',label=r'$x_1$')
plt.plot(m.time,(m.time-2)**2-2,'r--',label=r'$\hat x_1$')
plt.legend(loc='best')
plt.xlabel('Time')
plt.ylabel('Value')
plt.show()
One issue is that x1(1-e8)+x1(2-1e-8)=0 is used as a constraint instead of x1(0)+x1(2)=0. The numerical solutions should be nearly equivalent and the 1e-8 can be further reduced.
I copied and pasted the code from https://apmonitor.com/pdc/index.php/Main/ArduinoEstimation2?action=sourceblock&num=12 and I commented out the filename='data.csv' so that it would use the information on the apmonitor website. The graph that it is producing is very different from the one posted. I tried doing gekko(remote=True) and also gekko(remote=false) but it gave the same issue. I also tried clearing the cache and restarting everything but it did the same thing. Did I copy and paste something wrong? Is there an issue with the Gekko server right now?
You may need to upgrade your Gekko version:
pip install gekko --upgrade
Here is the solution that I get with both remote=False (local computed) and remote=True (cloud computed):
U : 5.3262633524
Us : 6.8780901241
alpha1: 0.011754836102
alpha2: 0.006022948462
tau: 18.916289169
SAE Energy Balance: 499.55
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from gekko import GEKKO
from scipy.integrate import odeint
from scipy.interpolate import interp1d
# Import data
filename = 'https://apmonitor.com/pdc/uploads/Main/tclab_data2.txt'
data = pd.read_csv(filename)
# Fit Parameters of Energy Balance
m = GEKKO() # Create GEKKO Model
# Parameters to Estimate
U = m.FV(value=10,lb=1,ub=20)
Us = m.FV(value=20,lb=5,ub=40)
alpha1 = m.FV(value=0.01,lb=0.001,ub=0.03) # W / % heater
alpha2 = m.FV(value=0.005,lb=0.001,ub=0.02) # W / % heater
tau = m.FV(value=10.0,lb=5.0,ub=60.0)
# Measured inputs
Q1 = m.Param()
Q2 = m.Param()
Ta =23.0+273.15 # K
mass = 4.0/1000.0 # kg
Cp = 0.5*1000.0 # J/kg-K
A = 10.0/100.0**2 # Area not between heaters in m^2
As = 2.0/100.0**2 # Area between heaters in m^2
eps = 0.9 # Emissivity
sigma = 5.67e-8 # Stefan-Boltzmann
TH1 = m.SV()
TH2 = m.SV()
TC1 = m.CV()
TC2 = m.CV()
# Heater Temperatures in Kelvin
T1 = m.Intermediate(TH1+273.15)
T2 = m.Intermediate(TH2+273.15)
# Heat transfer between two heaters
Q_C12 = m.Intermediate(Us*As*(T2-T1)) # Convective
Q_R12 = m.Intermediate(eps*sigma*As*(T2**4-T1**4)) # Radiative
# Energy balances
m.Equation(TH1.dt() == (1.0/(mass*Cp))*(U*A*(Ta-T1) \
+ eps * sigma * A * (Ta**4 - T1**4) \
+ Q_C12 + Q_R12 \
+ alpha1*Q1))
m.Equation(TH2.dt() == (1.0/(mass*Cp))*(U*A*(Ta-T2) \
+ eps * sigma * A * (Ta**4 - T2**4) \
- Q_C12 - Q_R12 \
+ alpha2*Q2))
# Conduction to temperature sensors
m.Equation(tau*TC1.dt() == TH1-TC1)
m.Equation(tau*TC2.dt() == TH2-TC2)
# Options
# STATUS=1 allows solver to adjust parameter
U.STATUS = 1
Us.STATUS = 1
alpha1.STATUS = 1
alpha2.STATUS = 1
tau.STATUS = 1
Q1.value=data['Q1'].values
Q2.value=data['Q2'].values
TH1.value=data['T1'].values[0]
TH2.value=data['T2'].values[0]
TC1.value=data['T1'].values
TC1.FSTATUS = 1 # minimize fstatus * (meas-pred)^2
TC2.value=data['T2'].values
TC2.FSTATUS = 1 # minimize fstatus * (meas-pred)^2
m.time = data['Time'].values
m.options.IMODE = 5 # MHE
m.options.EV_TYPE = 2 # Objective type
m.options.NODES = 2 # Collocation nodes
m.options.SOLVER = 3 # IPOPT
m.solve(disp=False) # Solve
# Parameter values
print('U : ' + str(U.value[0]))
print('Us : ' + str(Us.value[0]))
print('alpha1: ' + str(alpha1.value[0]))
print('alpha2: ' + str(alpha2.value[-1]))
print('tau: ' + str(tau.value[0]))
sae = 0.0
for i in range(len(data)):
sae += np.abs(data['T1'][i]-TC1.value[i])
sae += np.abs(data['T2'][i]-TC2.value[i])
print('SAE Energy Balance: ' + str(sae))
# Create plot
plt.figure(figsize=(10,7))
ax=plt.subplot(2,1,1)
ax.grid()
plt.plot(data['Time'],data['T1'],'r.',label=r'$T_1$ measured')
plt.plot(m.time,TC1.value,color='black',linestyle='--',\
linewidth=2,label=r'$T_1$ energy balance')
plt.plot(data['Time'],data['T2'],'b.',label=r'$T_2$ measured')
plt.plot(m.time,TC2.value,color='orange',linestyle='--',\
linewidth=2,label=r'$T_2$ energy balance')
plt.ylabel(r'T ($^oC$)')
plt.legend(loc=2)
ax=plt.subplot(2,1,2)
ax.grid()
plt.plot(data['Time'],data['Q1'],'r-',\
linewidth=3,label=r'$Q_1$')
plt.plot(data['Time'],data['Q2'],'b:',\
linewidth=3,label=r'$Q_2$')
plt.ylabel('Heaters')
plt.xlabel('Time (sec)')
plt.legend(loc='best')
plt.savefig('tclab_2nd_order_physics.png')
plt.show()
I am trying to use GEKKO on PYTHON to control the level of a CSTR tank while manipulating the inlet flow q. I tried the same problem using a pid controller and it worked. However, on GEKKO the height is not tracking its setpoint. Once I did the doublet test: at a flow rate of 200, the height reached 800 and as I decreased the flowrate to 2, the height was about 0. However, when im putting the height setpoint in GEKKO as 700 or 800, the flowrate is not stopping at 200, it is continuously increasing indefinitely.
Below is my code:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
from gekko import GEKKO
# Steady State Initial Condition
u2_ss=100.0
h_ss=399.83948182
x0 = np.empty(1)
x0[0]= h_ss
#%% GEKKO nonlinear MPC
m = GEKKO(remote=False)
m.time = [0,0.02,0.04,0.06,0.08,0.1,0.12,0.15,0.2]
c1=5.0
Ac=10.0
# initial conditions
h0=399.83948182
q0=100.0
m.h= m.CV(value=h0)
m.q=m.MV(value=q0,lb=0,ub=100000)
m.Equation(m.h.dt()==(m.q-c1*m.h**0.5)/Ac)
#MV tuning
m.q.STATUS = 1
m.q.FSTATUS = 0
m.q.DMAX = 100
m.q.DMAXHI = 100
m.q.DMAXLO = -100
#CV tuning
DT = 0.5 # deadband
m.h.STATUS = 1
m.h.FSTATUS = 1
m.h.TR_INIT = 1
m.h.TAU = 1.0
m.options.CV_TYPE = 1
m.options.IMODE = 6
m.options.SOLVER = 3
#%% define CSTR model
def cstr(x,t,u2,Ac):
q=u2
c1=5.0
Ac=10.0
# States (2):
# the height of the tank (m)
h=x[0]
# Parameters:
# Calculate height derivative
dhdt=(q-c1*h**0.5)/Ac
if x[0]>=1000 and dhdt>0:
dh1dt = 0
# Return xdot:
xdot = np.zeros(1)
xdot[0]= dhdt
return xdot
# Time Interval (min)
t = np.linspace(0,20,100)
# Store results for plotting
hsp=np.ones(len(t))*h_ss
h=np.ones(len(t))*h_ss
u2 = np.ones(len(t)) * u2_ss
# Set point steps
hsp[0:50] = 500.0
hsp[100:150]=700.0
# Create plot
plt.figure(figsize=(10,7))
plt.ion()
plt.show()
# Simulate CSTR
for i in range(len(t)-1):
# simulate one time period (0.05 sec each loop)
ts = [t[i],t[i+1]]
y = odeint(cstr,x0,ts,args=(u2[i+1],Ac))
# retrieve measurements
h[i+1]= y[-1][0]
# insert measurement
m.h.MEAS=h[i+1]
# solve MPC
m.solve(disp=True)
m.h.SPHI = hsp[i+1] + DT
m.h.SPLO = hsp[i+1] - DT
# retrieve new q value
u2[i+1] = m.q.NEWVAL
# update initial conditions
x0[0]= h[i+1]
#%% Plot the results
plt.clf()
plt.subplot(2,1,1)
plt.plot(t[0:i],u2[0:i],'b--',linewidth=3)
plt.ylabel('inlet flow')
plt.subplot(2,1,2)
plt.plot(t[0:i],hsp[0:i],'g--',linewidth=3,label=r'$h_{sp}$')
plt.plot(t[0:i],h[0:i],'k.-',linewidth=3,label=r'$h_{meas}$')
plt.xlabel('time')
plt.ylabel('tank level')
plt.legend(loc='best')
plt.draw()
plt.pause(0.01)
Mohamad,
If you want to simulate a tank level control, please start with this code with the volumetric flowrate of inlet(qin, MV) and outlet(qout, DV).
m = GEKKO(remote=False)
m.time = [0,0.02,0.04,0.06,0.08,0.1,0.12,0.15,0.2]
h0=50
q0=10
Ac=30 # m2,Cross section Area
m.h= m.CV(value=h0)
m.qin = m.MV(value=q0,lb=0,ub=100)
m.qout = m.Param(value=5)
m.Equation(m.h.dt() == (m.qin - m.qout)/Ac)
I am trying to plot the phase potrait for the equation as defined in my sh2 function in the code below. I know the expected phase plot should be [[expected phase plot][1]][1] [1]: https://i.stack.imgur.com/y1T9Y.png.
However this is my result: [[result][1]: https://i.stack.imgur.com/EuSOm.png.
I am using integrate.odeint can anyone suggest what I could change in the could below or if there would be best to use another algorithm that would give me a closer result to th expected.
Please find my code :
import matplotlib.pyplot as plt
import numpy as np
from numpy import sin
import scipy.integrate as integrate
from math import *
g = 9.81
l = 1.6
l_big = 2.0
l_small = 1.6
m = 0.01
alpha = l_big-l_small
k = 10*(10**40)
def sh2(r1,t):
theta1,omega1 = r1
sh2_theta1 = omega1
sh2_omega1 = -g*(l + ((1/2)*alpha*(1-np.tanh(theta1*omega1*k))))*sin(theta1)
return np.array([sh2_theta1, sh2_omega1],float)
init_state = np.radians([30.0,0])
dt = 1/10.0
time = np.arange(0,10.0,dt)
timexo = np.arange(0,10.0,dt)
state2 = integrate.odeint(sh2,init_state,time)
print(len(state2),len(timexo))
state2_plot = np.transpose(state2[0:2500])
plt.plot(timexo[0:2500],state2_plot[1], '--m', label = r'$\theta = \frac{\pi}{6}$')
plt.xlabel('Time t (s) ')
plt.ylabel('Angular Velocity' ' ' r'$\dot{\theta}$')
plt.show()
#code for phase plot
# initial values
x_0 = 0.0 # intial angular position
v_0 = 1.0 # initial angular momentum
t_0 = 0 # initial time
# initial y-vector from initial position and momentum
y0 = np.array([x_0,v_0])
# max value of time and points in time to integrate to
t_max = 10
N_spacing_in_t = 10000
# create vector of time points you want to evaluate
t = np.linspace(t_0,t_max,N_spacing_in_t)
# create vector of positions for those times
y_result = integrate.odeint(sh2, init_state, t)
# get angle and angular momentum
angle = y_result[:,0]
angular_velocity = y_result[:,1]
# plot result
fig = plt.figure()
plt.plot(angle, angular_velocity,'--k',lw=1)
plt.xlabel('Angle' ' ' r'$\theta$')
plt.ylabel(r'Angular Velocity' r' $\dot{\theta}$')
plt.gcf().savefig('pumping.png',dpi=300)
plt.show()
Thank you for tour time