Gekko Performance for long time horizon - gekko

In the code below (PV slope optimization over for the entire year - hourly time step, CSV data download link), is there any way to speed up the optimization's performance? Did I model inefficiently? If I set up the "days_to_consider" variable in the code as a low number of days (e.g., 14 days), the optimization can be done relatively fast, but the "days_to_consider" variable is increased to 180+ days, my computer doesn't find the solution.
It is important for me to obtain the solution fast because what I try to do eventually is to simulate the optimal control in microgrids (PV, building, EV, electric battery, etc.).
My code is shown below.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
import csv
m = GEKKO(remote=False)
days_to_consider = 14 # number of days to optimize slope (14 days are ok, 365 days can't be solved)
m.time = np.linspace(0, 24*days_to_consider-1, 24*days_to_consider) # Hourly time step
# Read the weather data from CSV
with open("PV_Input.csv", encoding='utf-8-sig') as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',')
inputs = [row for row in csv.reader(csv_file)]
#initialize variables
Eb_raw = []
beta_raw = [] # unit: radians
phi_raw = []
Ed_raw = []
Et_r_raw = []
for i in range(2,24*days_to_consider+2):
Eb_raw.append(float(inputs[i][1])) # solar beam radiation
beta_raw.append(float(inputs[i][6])) # solar altitude
phi_raw.append(float(inputs[i][7])) # solar azimuth
Ed_raw.append(float(inputs[i][4])) # solar diffuse radiation
Et_r_raw.append(float(inputs[i][5])) # solar ground reflected
# Assign the time-dependent coefficients
Eb = m.Param(value=Eb_raw)
beta = m.Param(value=beta_raw)
phi = m.Param(value=phi_raw)
Ed = m.Param(value=Ed_raw)
Et_r = m.Param(value=Et_r_raw)
azimuth = 0.0 # assumed azimuth is fixed, unit: rad
rho_g = 0.14 # reflectance
area = 100 # PV area
P_pk = 250 # peak power
p_factor = 0.8 # performance factor
misc = m.Param(value=area * P_pk * p_factor/1000) # area * peak power * performance factor / 1000
# Initialize variables
slope = m.MV(value=0.9225608396276507861, lb=0.0, ub=1.5708) # unit: radian
slope.STATUS = 1
slope.DCOST = 1 # penalty for unnecessary changes
slope.DMAX = 5 # for smooth slope changes
PV_elec = m.SV()
# build PV Equation
cos_theta = m.Intermediate(m.cos(beta)*(m.cos(phi)*m.cos(azimuth)+m.sin(phi)*m.sin(azimuth))*m.sin(slope)+m.sin(beta)*m.cos(slope))
gamma = m.Intermediate(m.max3(0.45, 0.55+0.437*cos_theta+0.313*(cos_theta)**2))
m.Equation(PV_elec == misc*(Eb*(m.cos(beta)*m.cos(phi)*m.cos(azimuth)*m.sin(slope) \
+ m.cos(beta)*m.sin(phi)*m.sin(azimuth)*m.sin(slope)\
+ m.sin(beta)*m.cos(slope))\
+ Ed*(gamma*m.sin(slope) + m.cos(slope))\
+ 0.5*rho_g*(1-m.cos(slope))*(Eb*m.sin(beta)+Ed)))
m.Maximize(PV_elec)
m.options.IMODE = 6 # Optimal control
m.options.SOLVER = 1
m.solve(disp=True)
# Unit conversion to degree
conversion_rad_to_deg = 180/3.14159265359
slope_in_degree = [i*conversion_rad_to_deg for i in slope]
# Plot the results
plt.subplot(2,1,1)
plt.plot(m.time, PV_elec, 'k')
plt.ylabel('PV Power [kW]')
plt.subplot(2,1,2)
plt.step(m.time, slope_in_degree,'r')
plt.ylabel('slope [deg]')
plt.xlabel('Time [hr]')
plt.show()

Part of the reason that this problem is challenging is that it is a mixed integer nonlinear programming problem with max3. The max3 function is used to clip 0.55+0.437*cos_theta+0.313*(cos_theta)**2 at the lower bound of 0.45.
The dynamics in your problem are the DMAX constraint that limits how fast the angle can change. There are no differential equations in your problem. If there were no DMAX constraint then you could solve each time period separately. For the 14 day period, your solution is:
It solves with the APOPT solver in about 17 seconds.
Number of state variables: 3350
Number of total equations: - 2680
Number of slack variables: - 670
---------------------------------------
Degrees of freedom : 0
----------------------------------------------
Dynamic Control with APOPT Solver
----------------------------------------------
Iter: 1 I: 0 Tm: 16.77 NLPi: 76 Dpth: 0 Lvs: 0 Obj: -1.24E+06 Gap: 0.00E+00
Successful solution
---------------------------------------------------
Solver : APOPT (v1.0)
Solution time : 16.814799999999998 sec
Objective : -1237075.78834978
Successful solution
---------------------------------------------------
If you reformulate the problem with a slack variable and complementarity constraint then it solves much faster (2.9 seconds) and gives nearly the same solution.
gamma = m.Var(0.5,lb=0.45)
slk = m.Var(lb=0); m.Minimize(1e-3*slk)
m.Equation(slk*(gamma-0.45)<1e-3)
m.Equation(gamma==0.55+0.437*cos_theta+0.313*(cos_theta)**2+slk)
It now solves over the entire year (365 days) and requires 133 seconds to solve the problem with 78,831 variables and 61,313 equations.
Number of state variables: 78831
Number of total equations: - 61313
Number of slack variables: - 8759
---------------------------------------
Degrees of freedom : 8759
EXIT: Optimal Solution Found.
The solution was found.
The final value of the objective function is -4.464484997593126E+7
---------------------------------------------------
Solver : IPOPT (v3.12)
Solution time : 132.942 sec
Objective : -4.464484990790293E+7
Successful solution
---------------------------------------------------
Here is the complete script.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
import csv
m = GEKKO(remote=False)
#(14 days are ok, 365 days can't be solved)
days_to_consider = 365 # number of days to optimize slope
# Hourly time step
m.time = np.linspace(0, 24*days_to_consider-1, \
24*days_to_consider)
# Read the weather data from CSV
with open("PV_Input.csv", encoding='utf-8-sig') as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',')
inputs = [row for row in csv.reader(csv_file)]
#initialize variables
Eb_raw = []
beta_raw = [] # unit: radians
phi_raw = []
Ed_raw = []
Et_r_raw = []
for i in range(2,24*days_to_consider+2):
Eb_raw.append(float(inputs[i][1])) # solar beam radiation
beta_raw.append(float(inputs[i][6])) # solar altitude
phi_raw.append(float(inputs[i][7])) # solar azimuth
Ed_raw.append(float(inputs[i][4])) # solar diffuse radiation
Et_r_raw.append(float(inputs[i][5])) # solar ground reflected
# Assign the time-dependent coefficients
Eb = m.Param(value=Eb_raw)
beta = m.Param(value=beta_raw)
phi = m.Param(value=phi_raw)
Ed = m.Param(value=Ed_raw)
Et_r = m.Param(value=Et_r_raw)
azimuth = 0.0 # assumed azimuth is fixed, unit: rad
rho_g = 0.14 # reflectance
area = 100 # PV area
P_pk = 250 # peak power
p_factor = 0.8 # performance factor
# area * peak power * performance factor / 1000
misc = m.Param(value=area * P_pk * p_factor/1000)
# Initialize variables
# unit: radian
slope = m.MV(value=0.9225608396276507861, lb=0.0, ub=1.5708)
slope.STATUS = 1
slope.DCOST = 1 # penalty for unnecessary changes
slope.DMAX = 5 # for smooth slope changes
PV_elec = m.SV()
# build PV Equation
cos_theta = m.Intermediate(m.cos(beta)*(m.cos(phi)\
*m.cos(azimuth)+m.sin(phi)*m.sin(azimuth))\
*m.sin(slope)+m.sin(beta)*m.cos(slope))
gamma = m.Var(0.5,lb=0.45)
slk = m.Var(lb=0); m.Minimize(1e-3*slk)
m.Equation(slk*(gamma-0.45)<1e-3)
m.Equation(gamma==0.55+0.437*cos_theta+0.313*(cos_theta)**2+slk)
m.Equation(PV_elec == misc*(Eb*(m.cos(beta)\
*m.cos(phi)*m.cos(azimuth)*m.sin(slope) \
+ m.cos(beta)*m.sin(phi)*m.sin(azimuth)*m.sin(slope)\
+ m.sin(beta)*m.cos(slope))\
+ Ed*(gamma*m.sin(slope) + m.cos(slope))\
+ 0.5*rho_g*(1-m.cos(slope))*(Eb*m.sin(beta)+Ed)))
m.Maximize(PV_elec)
m.options.IMODE = 6 # Optimal control
m.options.SOLVER = 3
#m.options.COLDSTART = 1
#m.solve(disp=True)
m.options.COLDSTART = 0
#m.options.TIME_SHIFT = 0
m.solve(disp=True)
# Unit conversion to degree
conversion_rad_to_deg = 180/3.14159265359
slope_in_degree = [i*conversion_rad_to_deg for i in slope]
# Plot the results
plt.subplot(3,1,1)
plt.plot(m.time, PV_elec, 'k')
plt.ylabel('PV Power [kW]')
plt.subplot(3,1,2)
plt.step(m.time, gamma,'b')
plt.plot([0,max(m.time)],[0.45,0.45],'k:')
plt.ylabel('gamma')
plt.subplot(3,1,3)
plt.step(m.time, slope_in_degree,'r')
plt.ylabel('slope [deg]')
plt.xlabel('Time [hr]')
plt.show()

Related

Use Gekko and Python to fit a numerical ODE solution to data

Use Gekko to fit a numerical ODE solution to data.
Hi everyone!
I was wondering, if it is possible to fit coefficients of an ODE using GEKKO.
I unsuccessfully tried to replicate the example given here.
This is what I have come up with (but is flawed – and I should perhaps mention that my math skills are unfortunately rather poor):
import numpy as np
from gekko import GEKKO
tspan = [0, 0.1, 0.2, 0.4, 0.8, 1]
Ca_data = [2.0081, 1.5512, 1.1903, 0.7160, 0.2562, 0.1495]
m = GEKKO(remote=False)
t = m.Param(value=tspan)
m.time = t
Ca_m = m.Param(value=Ca_data)
Ca = m.Var()
k = m.FV(value=1.3)
k.STATUS = 1
m.Equation( Ca.dt() == -k * Ca)
m.Obj( ((Ca-Ca_m)**2)/Ca_m )
m.options.IMODE = 2
m.solve(disp=True)
print(k.value[0]) #2.58893455 is the solution
Can someone help me out here?
Thank you very much,
Martin
(This is my first post here – please be gentle, if I have done something not appropriate.)
Your solution was close but you needed:
More NODES (default=2) to improve the accuracy. Gekko only adds that points that you define. See additional information on collocation.
Define Ca as m.CV() to use built-in error model instead of m.Var() and m.Obj with NODES>=3. Otherwise, the internal nodes of each collocation interval are also matched to the measurements and this gives a slightly wrong answer.
Set EV_TYPE=2 to use a squared error. An absolute value objective EV_TYPE=1 (default) gives a correct but slightly different answer.
import numpy as np
from gekko import GEKKO
m = GEKKO(remote=False)
m.time = [0, 0.1, 0.2, 0.4, 0.8, 1]
Ca_data = [2.0081, 1.5512, 1.1903, 0.7160, 0.2562, 0.1495]
Ca = m.CV(value=Ca_data); Ca.FSTATUS = 1 # fit to measurement
k = m.FV(value=1.3); k.STATUS = 1 # adjustable parameter
m.Equation(Ca.dt()== -k * Ca) # differential equation
m.options.IMODE = 5 # dynamic estimation
m.options.NODES = 5 # collocation nodes
m.options.EV_TYPE = 2 # squared error
m.solve(disp=True) # display solver output
print(k.value[0]) # 2.58893455 is the curve_fit solution
The solution is k=2.5889717102. A plot shows the match to the measured values.
import matplotlib.pyplot as plt # plot solution
plt.plot(m.time,Ca_data,'ro')
plt.plot(m.time,Ca.value,'bx')
plt.show()
There are additional tutorials and course material on parameter estimation with differential and algebraic equation models.

Kalman FIlter Convergence

Attached is a simple python Kalman filter example of a free-fall object (g=-9.8m/s^2)
Alas, I have a problem. The state vector x contains both the position and the velocity but the z vector (measurement) contains only the position.
If I set a wrong initial position value, the algorithm coverages to the true value even with noisy measurements (see picture below)
However, if I sent the wrong initial velocity value, the algorithm does not converge even though the motion model is defined correctly.
Attached is the python code:
kalman.py
In your code I see two problems.
You set the Q-Matrix to zero. It means you trust too much in your model and give the filter no chance to improve the estimation through the measurement. Your filter becomes to stiff. You can think of it like a low pass filter with a very big time constant.
In my code I set the Q-Matrix to
Q = np.array([[1,0],[0,0.1]])
The second issue is your measurement noise. You simulate the noisy measurements with R=100 but communicate to the filter R=4. The filter trusts the measurement more than it should be. This issue is not really relevant to your question but still it should be corrected.
Now even if I set the initial velocity to 20, the position estimation works fine.
Here is the estimation for R = 4:
And for R = 100:
UPDATE
The velocity estimation works wrong, because you have some mistakes in your matrix operations. Please note, the matrix multiplication goes through np.dot(), not through *.
Here is a correct result for v0 = 20:
Many thanks, Anton.
Attached below is the corrected code for your convenience:
Roi
import numpy as np
import matplotlib.pyplot as plt
%matplotlib notebook
from numpy.linalg import inv
N = 1000 # number of time steps
dt = 0.01 # Sampling time (s)
t = dt*np.arange(N)
F = np.array([[1, dt],[ 0, 1]])# system matrix - state
B = np.array([[-1/2*dt**2],[ -dt]])# system matrix - input
H = np.array([[1, 0]])#; % observation matrix
Q = np.array([[1,0],[0,1]])
u = 9.80665# % input = acceleration due to gravity (m/s^2)
I = np.array([[1,0],[0,1]]) #identity matrix
# Define the initial position and velocity
y0 = 100; # m
v0 = 0; # m/s
G2 = np.array([-1/2*dt**2, -dt])# system matrix - input
# Initialize the state vector (true state)
xt = np.zeros((2, N)) # True state vector
xt[:,0] = [y0,v0]
for k in range(1,N):
xt[:,k] = np.dot(F,xt[:,k-1]) +G2*u
#Generate the noisy measurement from the true state
R = 4 # % m^2/s^2
v = np.sqrt(R)*np.random.randn(N) #% measurement noise
z = np.dot(H,xt) + v; #% noisy measurement
R2=4
#% Initialize the covariance matrix
P = np.array([[10, 0], [0, 0.1]])# Covariance for initial state error
#% Loop through and perform the Kalman filter equations recursively
x_list =[]
x_kalman= np.array([[117],[290]])
x_list.append(x_kalman)
print(-B*u)
for k in range(1,N):
x_kalman=np.dot(F,x_kalman) +B*u
P = np.dot(np.dot(F,P),F.T) +Q
S=(np.dot(np.dot(H,P),H.T) + R2)
S2 = inv(S)
K = np.dot(P,H.T)*S2
x_kalman = x_kalman +K*((z[:,k]- np.dot(H,x_kalman)))
P = np.dot((I - K*H),P)
x_list.append(x_kalman)
x_array = np.array(x_list)
print(x_array.shape)
plt.figure()
plt.plot(t,z[0,:], label="measurment", color='LIME', linewidth=1)
plt.plot(t,x_array[:,0,:],label="kalman",linewidth=5)
plt.plot(t,xt[0,:],linestyle='--', label = "Truth",linewidth=6)
plt.legend(fontsize=30)
plt.grid(True)
plt.xlabel("t[s]")
plt.title("Position Estimation", fontsize=20)
plt.ylabel("$X_t$ = h[m]")
plt.gca().set( ylim=(0, 110))
plt.gca().set(xlim=(0,6))
plt.figure()
#plt.plot(t,z, label="measurment", color='LIME')
plt.plot(t,x_array[:,1,:],label="kalman",linewidth=4)
plt.plot(t,xt[1,:],linestyle='--', label = "Truth",linewidth=2)
plt.legend()
plt.grid(True)
plt.xlabel("t[s]")
plt.title("Velocity Estimation")
plt.ylabel("$X_t$ = h[m]")

How to use a vector set point with mpc inorder to give program information on how the future set point will change

I am using an MPC to run a heater system. Currently I have it take an individual value from my set point array at a given point in time to adjust the process to reach. I would like to be able to give it the current desired value and a couple of points in the future for the set point so that it can better adjust as the set point changes. How can I give gekko a vector in order to have it better adjust to future set points?
this is the part of my code that currently updates my setpoint values.
T1[i] = a.T1
T2[i] = a.T2
TC1.MEAS = T1[i]
TC2.MEAS = T2[i]
DT = .1
TC1.SPHI = sp1[i] + DT #sp1 and sp2 are set point arrays for the two heaters
TC1.SPLO = sp1[i] - DT
TC2.SPHI = sp2[i] + DT
TC2.SPLO = sp2[i] - DT
m.solve(disp=False)
The gekko CV object only uses a scalar value of an array for SP, SPHI, and SPLO so some modification is needed to have the optimizer consider future setpoint changes. A simple MPC application shows how setpoints are used in Gekko.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO()
m.time = np.linspace(0,20,41)
p = m.MV(value=0, lb=0, ub=100) # Declare MV
p.STATUS = 1 # allow optimizer to change
p.DCOST = 0.1 # smooth MV response
p.DMAX = 10.0 # max move each cycle
v = m.CV(value=0) # Declare CV
v.STATUS = 1 # add CV to the objective
m.options.CV_TYPE = 2 # squared error
v.SP = 40 # set point
v.TR_INIT = 1 # set point trajectory
v.TAU = 5 # time constant of trajectory
m.Equation(10*v.dt() == -v + 2*p)
m.options.IMODE = 6 # control
m.solve(disp=False)
# get additional solution information
import json
with open(m.path+'//results.json') as f:
results = json.load(f)
plt.figure()
plt.subplot(2,1,1)
plt.plot(m.time,p.value,'b-',label='MV Optimized')
plt.legend()
plt.ylabel('Input')
plt.subplot(2,1,2)
plt.plot(m.time,results['v1.tr'],'k-',label='Reference Trajectory')
plt.plot(m.time,v.value,'r--',label='CV Response')
plt.ylabel('Output')
plt.xlabel('Time')
plt.legend(loc='best')
plt.show()
There is one setpoint value that is a target of 40 and a reference trajectory is defined with a time constant of 5. The MPC is not able to follow the reference trajectory exactly because of a rate of change constraint with DMAX=10. There are two options if you'd like the optimizer to know about future setpoint changes.
Option 1: Don't use CV, Use Feedforward Parameter
If you don't need the reference trajectory and are okay with a squared error objective then then easiest method to project future setpoint changes is to define your own MPC objective with a feedforward parameter vector of setpoint values. The example problem shows that the optimizer is anticipating the setpoint change and proactively moves before the next setpoint change to minimize the overall sum of squared error. This may be desirable in many circumstances but may be undesirable in manufacturing where there are product grade changes and the end of the production campaign should be in-spec before producing transition material.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO()
m.time = np.linspace(0,20,41)
p = m.MV(value=0, lb=0, ub=100) # Declare MV
p.STATUS = 1 # allow optimizer to change
p.DCOST = 0.1 # smooth MV response
p.DMAX = 10.0 # max move constraint
v = m.Var(value=0)
sp = np.ones(41)*40
sp[20:] = 60
s = m.Param(value=sp)
m.Obj((s-v)**2)
m.Equation(10*v.dt() == -v + 2*p)
m.options.IMODE = 6 # control
m.solve(disp=False)
plt.figure()
plt.subplot(2,1,1)
plt.plot(m.time,p.value,'b-',label='MV Optimized')
plt.legend()
plt.ylabel('Input')
plt.subplot(2,1,2)
plt.plot(m.time,sp,'k-',label='Setpoint')
plt.plot(m.time,v.value,'r--',label='CV Response')
plt.ylabel('Output')
plt.xlabel('Time')
plt.legend(loc='best')
plt.show()
Option 2: Error as CV
If it is desirable to use the reference trajectory and Gekko built-in CV options, then an option is to define a new error variable e and control that instead. The error variable always has a setpoint of zero and the feedforward setpoint is implemented as a feedforward parameter.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO()
m.time = np.linspace(0,20,41)
p = m.MV(value=0, lb=0, ub=100) # Declare MV
p.STATUS = 1 # allow optimizer to change
p.DCOST = 0.1 # smooth MV response
p.DMAX = 10.0 # max move constraint
v = m.Var(value=0)
sp = np.ones(41)*40
sp[20:] = 60
s = m.Param(value=sp)
e = m.CV(value=0) # Declare CV
e.STATUS = 1 # add CV to the objective
m.options.CV_TYPE = 2 # squared error
e.SP = 0 # set point
e.TR_INIT = 1 # error trajectory
e.TAU = 5 # time constant of trajectory
m.Equation(e==s-v)
m.Equation(10*v.dt() == -v + 2*p)
m.options.IMODE = 6 # control
m.solve(disp=False)
plt.figure()
plt.subplot(2,1,1)
plt.plot(m.time,p.value,'b-',label='MV Optimized')
plt.legend()
plt.ylabel('Input')
plt.subplot(2,1,2)
plt.plot(m.time,sp,'k-',label='Setpoint')
plt.plot(m.time,v.value,'r--',label='CV Response')
plt.ylabel('Output')
plt.xlabel('Time')
plt.legend(loc='best')
plt.show()
For every time step, Gekko automatically generates the setpoint in the form of the array for the future control horizon. And, the arrays are usually filled with the single value that you assigned. However, you can give the setpoint as an array as shown below.
sp1 = np.array([[1,2,3,4,5],
[2,3,4,5,6],
[3,4,5,6,7],
[4,5,6,7,8]])
Then, you can assign the every row of your matrix for each time step just as you did in your question.
DT = .1
TC1.SPHI = sp1[i] + DT
Note:
You need to have the same length of setpoint array which means the size of the column in your setpoint matrix with the control horizon (e.g. 'm.time').
You might want to set the setpoint trajectory option '0' if you don't want to filter out your setpoint sequence in your array. (TR_INIT = 0)

Is it possible to get the data of biased and unbiased predicted controlled variables when using APMonitor for Model Predictive Control in Python?

I am trying to build a python code for Model Predictive Control using APMonitor. However, I don't want to get the results on an third party online server. Hence, I want to collect the data of the predicted biased and unbiased and plot them on Python myself.
Try this in Python Gekko:
# get additional solution information
import json
with open(m.path+'//results.json') as f:
results = json.load(f)
You can get the unbiased model result by getting the dictionary value of your variable v with v.name. You can get the biased model prediction with v.name+'.bcv'. Here is an example that also shows how to get the raw trajectory information.
This gives you access to the raw data. An example shows how to plot from the JSON data.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO()
m.time = np.linspace(0,20,41)
# Parameters
mass = 500
b = m.Param(value=50)
K = m.Param(value=0.8)
# Manipulated variable
p = m.MV(value=0, lb=0, ub=100)
p.STATUS = 1 # allow optimizer to change
p.DCOST = 0.1 # smooth out gas pedal movement
p.DMAX = 20 # slow down change of gas pedal
# Controlled Variable
v = m.CV(value=0)
v.STATUS = 1 # add the SP to the objective
m.options.CV_TYPE = 2 # squared error
v.SP = 40 # set point
v.TR_INIT = 1 # set point trajectory
v.TAU = 5 # time constant of trajectory
# Process model
m.Equation(mass*v.dt() == -v*b + K*b*p)
m.options.IMODE = 6 # control
m.solve(disp=False,GUI=True)
# get additional solution information
import json
with open(m.path+'//results.json') as f:
results = json.load(f)
plt.figure()
plt.subplot(2,1,1)
plt.plot(m.time,p.value,'b-',label='MV Optimized')
plt.legend()
plt.ylabel('Input')
plt.subplot(2,1,2)
plt.plot(m.time,results['v1.tr'],'k-',label='Reference Trajectory')
plt.plot(m.time,v.value,'r--',label='CV Response')
plt.ylabel('Output')
plt.xlabel('Time')
plt.legend(loc='best')
plt.show()

Using "past" values to define current values in GEKKO equation

I am writing the GEKKO equations to determine a vehicle's gear box ratio which depends on the vehicle's previous derivatives. Is there a way to set a variable to the time shifted value of another variable?
Ex:
v=0,[1,2,3,4,5]
shifted_v=[0,1,2,3,4]
where the square bracket is the horizon and v is a state variable defined by equations.
One of the easiest ways to shift data sets is to use the numpy.roll function.
import numpy as np
x = np.linspace(0,5,6)
y = np.roll(x,-1) # shift left
y[-1] = 6
z = np.roll(x,1) # shift right
z[0] = -1
print('x: ' + str(x))
print('y: ' + str(y))
print('z: ' + str(z))
You can apply this strategy using Gekko variables by using the .value property such as:
import numpy as np
from gekko import GEKKO
m = GEKKO()
m.time = np.linspace(0,5,6)
x = m.Param(value=m.time)
y = m.Param()
y.value = np.roll(x.value,-1)
y.value[-1] = 6
z = m.Param()
z.value = np.roll(x.value,1)
z.value[0] = -1
There is also a TIME_SHIFT feature in Gekko that automatically shifts values as if they were advancing in time. The TIME_SHIFT option controls how much the values are shifted with every solve. The time shift happens at the beginning of the solve. Here is a more complete example with a visualization of the result.
import numpy as np
from gekko import GEKKO
import matplotlib.pyplot as plt
m = GEKKO()
m.time = np.linspace(0,5,6)
x = m.Param(value=m.time)
y = m.Param()
y.value = np.roll(x.value,-1)
y.value[-1] = 6
z = m.Param()
z.value = np.roll(x.value,1)
z.value[0] = -1
s = m.Var()
m.Equation(s==x+y-z)
m.options.IMODE=4
m.solve()
plt.subplot(2,1,1)
plt.plot(m.time,x.value,label='x')
plt.plot(m.time,y.value,label='y')
plt.plot(m.time,z.value,label='z')
plt.legend()
# solve a second time
m.options.TIME_SHIFT = 1 # default is 1
m.solve()
plt.subplot(2,1,2)
plt.plot(m.time,x.value,label='x')
plt.plot(m.time,y.value,label='y')
plt.plot(m.time,z.value,label='z')
plt.legend()
plt.show()
From your question, it appears that you need to calculate the previous derivative of a variable. If you need to time shift a value during the calculation, not just in the initialization phase, then I would recommend a discrete state space model with a delay of 1 time step. The link provides an example of how to implement this with 4 steps of delay. You would want to modify the discrete state space matrices to have 1 step of delay between the derivative and gear-box ratio.

Resources