I want to use two inputs or more to create a more precise estimation of a variable. I already estimated it using only one input and one FOPDT equation, but when I try to add one more input and the respective k, tau and theta, along with another equation, i get "Solution Not Found" error. Can I create a system of equations this way?
More details about the solver output below. Even though I added more variables than equations to use more than one input, this made my problem have negative degrees of freedom.
--------- APM Model Size ------------
Each time step contains
Objects : 2
Constants : 0
Variables : 15
Intermediates: 0
Connections : 4
Equations : 6
Residuals : 6
Number of state variables: 1918
Number of total equations: - 2151
Number of slack variables: - 0
---------------------------------------
Degrees of freedom : -233
* Warning: DOF <= 0
**********************************************
Dynamic Estimation with Interior Point Solver
**********************************************
Info: Exact Hessian
******************************************************************************
This program contains Ipopt, a library for large-scale nonlinear optimization.
Ipopt is released as open source code under the Eclipse Public License (EPL).
For more information visit http://projects.coin-or.org/Ipopt
******************************************************************************
This is Ipopt version 3.12.10, running with linear solver ma57.
Number of nonzeros in equality constraint Jacobian...: 6451
Number of nonzeros in inequality constraint Jacobian.: 0
Number of nonzeros in Lagrangian Hessian.............: 1673
Exception of type: TOO_FEW_DOF in file "IpIpoptApplication.cpp" at line 891:
Exception message: status != TOO_FEW_DEGREES_OF_FREEDOM evaluated false: Too few degrees of freedom (rethrown)!
EXIT: Problem has too few degrees of freedom.
An error occured.
The error code is -10
---------------------------------------------------
Solver : IPOPT (v3.12)
Solution time : 2.279999999154825E-002 sec
Objective : 0.000000000000000E+000
Unsuccessful with error code 0
---------------------------------------------------
Creating file: infeasibilities.txt
Use command apm_get(server,app,'infeasibilities.txt') to retrieve file
#error: Solution Not Found
And here's the code
from gekko import GEKKO
import numpy as np
import pandas as pd
import plotly.express as px
d19jc = d19jc.dropna()
d19jcSlice = d19jc.loc['2019-10-22 05:30:00':'2019-10-22 09:30:00'] #jc22
d19jcSlice.index = pd.to_datetime(d19jcSlice.index)
d19jcSliceGroupMin = d19jcSlice.groupby(pd.Grouper(freq='T')).mean()
data = d19jcSliceGroupMin.dropna()
xdf1 = data['Cond_PID_SP']
xdf2 = data['Front_PID_SP']
ydf1 = data['Cond_Center_Top_TC']
xms1 = pd.Series(xdf1)
xm1 = np.array(xms1)
xms2 = pd.Series(xdf2)
xm2 = np.array(xms2)
yms = pd.Series(ydf1)
ym = np.array(yms)
xm_r = len(xm1)
tm = np.linspace(0,xm_r-1,xm_r)
m = GEKKO()
m.options.IMODE=5
m.time = tm; time = m.Var(0); m.Equation(time.dt()==1)
k1 = m.FV(lb=0.1,ub=5); k1.STATUS=1
tau1 = m.FV(lb=1,ub=300); tau1.STATUS=1
theta1 = m.FV(lb=0,ub=30); theta1.STATUS=1
k2 = m.FV(lb=0.1,ub=5); k2.STATUS=1
tau2 = m.FV(lb=1,ub=300); tau2.STATUS=1
theta2 = m.FV(lb=0,ub=30); theta2.STATUS=1
# create cubic spline with t versus u
uc1 = m.Var(xm1); tc1 = m.Var(tm); m.Equation(tc1==time-theta1)
m.cspline(tc1,uc1,tm,xm1,bound_x=False)
# create cubic spline with t versus u
uc2 = m.Var(xm2); tc2 = m.Var(tm); m.Equation(tc2==time-theta2)
m.cspline(tc2,uc2,tm,xm2,bound_x=False)
x1 = m.Param(value=xm1)
x2 = m.Param(value=xm2)
y = m.Var(value=ym)
yObj = m.Param(value=ym)
m.Equation(tau1*y.dt()+(y-ym[0])==k1 * (uc1-xm1[0]))
m.Equation(tau2*y.dt()+(y-ym[0])==k2 * (uc2-xm2[0]))
m.Minimize((y-yObj)**2)
m.options.EV_TYPE=2
print('solve start')
m.solve(disp=True)
print('k1: ', k1.value[0])
print('tau1: ', tau1.value[0])
print('theta1: ', theta1.value[0])
print('k2: ', k2.value[0])
print('tau2: ', tau2.value[0])
print('theta2: ', theta2.value[0])
df_plot = pd.DataFrame({'DateTime' : data.index,
'Cond_Center_Top_TC' : np.array(yObj),
'Fit Cond_Center_Top_TC - Train' : np.array(y),
figGekko = px.line(df_plot,
x='DateTime',
y=['Cond_Center_Top_TC','Fit Cond_Center_Top_TC - Train'],
labels={"value": "Degrees Celsius"},
title = "(Cond_PID_SP & Front_PID_SP) -> Cond_Center_Top_TC JC only - Train")
figGekko.update_layout(legend_title_text='')
figGekko.show()
You currently have only one variable and two equations.
y = m.Var(value=ym)
yObj = m.Param(value=ym)
m.Equation(tau1*y.dt()+(y-ym[0])==k1 * (uc1-xm1[0]))
m.Equation(tau2*y.dt()+(y-ym[0])==k2 * (uc2-xm2[0]))
You need to create two separate variables y1 and y2 for the two equations.
y1 = m.Var(value=ym)
y2 = m.Var(value=ym)
yObj = m.Param(value=ym)
m.Equation(tau1*y1.dt()+(y1-ym[0])==k1 * (uc1-xm1[0]))
m.Equation(tau2*y2.dt()+(y2-ym[0])==k2 * (uc2-xm2[0]))
You may also need to create two separate measurement vectors for ym1 and ym2 if you have different data for each.
Edit: Multiple Input, Single Output (MISO) System
For a MISO system, you need to adjust the equation as shown in the Process Dynamics and Control course.
y = m.Var(value=ym)
yObj = m.Param(value=ym)
m.Equation(tau*y.dt()+(y-ym[0])==k1 * (uc1-xm1[0]) + k2 * (uc2-xm2[0]))
There is only one time constant in this form. If they need different time constants, you can also add the two outputs together with:
y1 = m.Var(value=ym[0])
y2 = m.Var(value=ym[0])
y = m.Var(value=ym)
yObj = m.Param(value=ym)
m.Equation(tau1*y1.dt()+(y1-ym[0])==k1 * (uc1-xm1[0]))
m.Equation(tau2*y2.dt()+(y2-ym[0])==k2 * (uc2-xm2[0]))
m.Equation(y==y1+y2)
In this case, y is the variable that has data and y1 and y2 are unmeasured states.
Please see the below example code for a FOPDT model that has two inputs and one output. Both the transfer function models have different FOPDT parameters, including different deadtimes as well.
It is still in a dynamic simulation mode (IMODE=4), but you can start from this modifying a little bit toward the dynamic estimation mode (IMODE=5) and MPC mode (IMODE=6) later.
from gekko import GEKKO
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
tf = 100
npt = 101
t = np.linspace(0,tf,npt)
u1 = np.zeros(npt)
u2 = np.zeros(npt)
u1[10:] = 5
u2[40:] = -5
m = GEKKO(remote=True)
m.time = t
time = m.Var(0)
m.Equation(time.dt()==1)
K1 = m.FV(1,lb=0,ub=1); K1.STATUS=1
tau1 = m.FV(5, lb=1,ub=300); tau1.STATUS=1
theta1 = m.FV(10, lb=2,ub=30); theta1.STATUS=1
K2 = m.FV(0.5,lb=0,ub=1); K2.STATUS=1
tau2 = m.FV(10, lb=1,ub=300); tau2.STATUS=1
theta2 = m.FV(20, lb=2,ub=30); theta2.STATUS=1
uc1 = m.Var(u1)
uc2 = m.Var(u2)
tc1 = m.Var(t)
tc2 = m.Var(t)
m.Equation(tc1==time-theta1)
m.Equation(tc2==time-theta2)
m.cspline(tc1,uc1,t,u1,bound_x=False)
m.cspline(tc2,uc2,t,u2,bound_x=False)
yp = m.Var()
yp1 = m.Var()
yp2 = m.Var()
m.Equation(yp1.dt() == -yp1/tau1 + K1*uc1/tau1)
m.Equation(yp2.dt() == -yp2/tau2 + K2*uc2/tau2)
m.Equation(yp == yp1 + yp2)
m.options.IMODE=4
m.solve()
print('K1: ', K1.value[0])
print('tau1: ', tau1.value[0])
print('theta1: ', theta1.value[0])
print('')
print('K2: ', K2.value[0])
print('tau2: ', tau2.value[0])
print('theta2: ', theta2.value[0])
plt.figure()
plt.subplot(2,1,1)
plt.plot(t,u1)
plt.plot(t,u2)
plt.legend([r'u1', r'u2'])
plt.ylabel('Inputs 1 & 2')
plt.subplot(2,1,2)
plt.plot(t,yp)
plt.legend([r'y1'])
plt.ylabel('Output')
plt.xlabel('Time')
plt.savefig('sysid.png')
plt.show()
K1: 1.0
tau1: 5.0
theta1: 10.0
K2: 0.5
tau2: 10.0
theta2: 20.0
Related
I'm trying to model a biodiesel reactor on python using GEKKO, but I'm not getting obtain the correct temperature profile, the Manipulated Variable doesn't change the value as expected... Here is my code:
import numpy as np
import matplotlib.pyplot as plt
from gekko import GEKKO
m = GEKKO()
nt = 1001
m.time = np.linspace(0,100,nt)
# Parameters
T = m.MV(value=321,ub=338,lb=298)
T.STATUS = 1
T.DCOST = 0.01
T.DMAX = 20
# Variables
CTG = m.Var(value=0.3226)
CDG = m.Var(value=0)
CMG = m.Var(value=0)
CE = m.Var(value=0)
CA = m.Var(value=1.9356)
CG = m.Var(value=0)
p = np.zeros(nt)
p[-1] = 1.0
final = m.Param(value=p)
# Intermediates
k1 = m.Intermediate(3.92e7*m.exp(-6614.83/T))
k2 = m.Intermediate(5.77e5*m.exp(-4997.98/T))
k3 = m.Intermediate(5.88e12*m.exp(-9993.96/T))
k4 = m.Intermediate(0.98e10*m.exp(-7366.64/T))
k5 = m.Intermediate(5.35e3*m.exp(-3231.18/T))
k6 = m.Intermediate(2.15e4*m.exp(-4824.87/T))
# Equations
m.Equation(CTG.dt()== -k1*CTG*CA + k2*CDG*CE)
m.Equation(CDG.dt()== k1*CTG*CA - k2*CDG*CE - k3*CDG*CA + k4*CMG*CE)
m.Equation(CMG.dt()== k3*CDG*CA - k4*CMG*CE - k5*CMG*CA + k6*CG*CE)
m.Equation(CE.dt()== k1*CTG*CA - k2*CDG*CE + k3*CDG*CA - k4*CMG*CE + k5*CMG*CA - k6*CG*CE)
m.Equation(CA.dt()== -CE.dt())
m.Equation(CG.dt()== k5*CMG*CA - k6*CG*CE)
# Objective Function
m.Maximize(CE*final)
m.options.IMODE = 9
m.solve()
print('Objective: ' + str(CE[-1]))
plt.figure(1)
plt.subplot(2,1,1)
plt.plot(m.time,CE.value,'k:',lw=2,label=r'$C_E$')
plt.plot(m.time,CTG.value,'b-',lw=2,label=r'$C_TG$')
plt.plot(m.time,CA.value,'g-',lw=2,label=r'$C_A$')
plt.ylabel('Value')
plt.legend(loc='best')
plt.figure(2)
plt.subplot(2,1,2)
plt.plot(m.time,T.value,'r--',lw=2,label=r'$Temp$')
plt.legend(loc='best')
plt.xlabel('Time')
plt.ylabel('Value')
plt.show()
I need to obtain a temperature profile like this:
A sensitivity study reveals that the optimal solution is to maximize the temperature. The problem with the code in your question is the selection of IMODE=9. Use IMODE=6 with less time points for a more reliable solution. The sequential optimization mode (9) is not as reliable as the simulataneous mode (6).
Here is a simulation at different temperatures with the temperature status off.
T.STATUS = 0
The simulation sensitivity study shows that there is an effect of temperature on the final objective. Using T.STATUS=1 with different initial conditions also reveals that higher temperature gives a more optimal solution. The only effect of the initial condition is that it requires one or two steps to get up to the maximum allowable temperature.
import numpy as np
import matplotlib.pyplot as plt
from gekko import GEKKO
m = GEKKO()
nt = 101
m.time = np.linspace(0,100,nt)
# Parameters
T = m.MV(value=321,ub=338,lb=298)
T.STATUS = 1
T.DCOST = 0 #0.01
T.DMAX = 20
# Variables
CTG = m.Var(value=0.3226)
CDG = m.Var(value=0)
CMG = m.Var(value=0)
CE = m.Var(value=0)
CA = m.Var(value=1.9356)
CG = m.Var(value=0)
p = np.zeros(nt)
p[-1] = 1.0
final = m.Param(value=p)
# Intermediates
k1 = m.Intermediate(3.92e7*m.exp(-6614.83/T))
k2 = m.Intermediate(5.77e5*m.exp(-4997.98/T))
k3 = m.Intermediate(5.88e12*m.exp(-9993.96/T))
k4 = m.Intermediate(0.98e10*m.exp(-7366.64/T))
k5 = m.Intermediate(5.35e3*m.exp(-3231.18/T))
k6 = m.Intermediate(2.15e4*m.exp(-4824.87/T))
# Equations
m.Equation(CTG.dt()== -k1*CTG*CA + k2*CDG*CE)
m.Equation(CDG.dt()== k1*CTG*CA - k2*CDG*CE - k3*CDG*CA + k4*CMG*CE)
m.Equation(CMG.dt()== k3*CDG*CA - k4*CMG*CE - k5*CMG*CA + k6*CG*CE)
m.Equation(CE.dt()== k1*CTG*CA - k2*CDG*CE + k3*CDG*CA - k4*CMG*CE + k5*CMG*CA - k6*CG*CE)
m.Equation(CA.dt()== -CE.dt())
m.Equation(CG.dt()== k5*CMG*CA - k6*CG*CE)
# Objective Function
m.Maximize(CE*final)
m.options.IMODE=6
m.options.NODES=3
m.options.SOLVER=3
plt.figure()
m.options.TIME_SHIFT=0
for Ti in [300,305,310,315,320,325,330,335]:
T.value=Ti
m.solve(disp=False)
plt.subplot(2,1,1)
plt.plot(m.time,CE.value,lw=2,label=r'$C_E$ # $T_0$='+str(Ti))
plt.subplot(2,1,2)
plt.plot(m.time,T.value,lw=2,label=r'$T$ # $T_0$='+str(Ti))
print(Ti,'K Objective: ' + str(CE[-1]))
plt.ylabel('Value')
plt.legend(loc='best')
plt.xlabel('Time')
plt.show()
I would recommend that you feed in the optimal solution that comes from the publication as an initial guess to see if that improves the optimization result. There may be a difference in equations or objective function that gives the different results.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO() # initialize gekko
nt=200
m.time = np.linspace(0,1,nt)
# Variables
x1 = m.Var(value=20,lb=0,)
x2 = m.Var(value=0,lb=0,)
x3 = m.Var(value=0,lb=0,)
p = np.zeros(nt) # mark final time point
p[-1] = 1.0
# optimize final time
tf = m.FV(value=0.7,lb=0.001,ub=100.0)
tf.STATUS = 1
# Equations
m.Equation(x1.dt() == -5*x1*tf)
m.Equation(x2.dt() == (5* x1 - 0.5*x2)*tf)
m.Equation(x3.dt() == 0.5*x2*tf)
#Minifunc
#m.Minimize(tf)
m.Maximize(x2)
#options
m.options.IMODE = 6
m.options.SOLVER = 3 #IPOPT optimizer
m.options.RTOL = 1E-8
m.options.OTOL = 1E-8
m.options.NODES = 5
m.solve(disp=True,debug=1)
print('Final Time: ' + str(tf.value[0]))
#Plotting stuff
tm = np.linspace(0,tf.value[0],nt)
plt.figure(1) # plot results
plt.plot(tm, x1.value, "k-", label=r"$x_1$")
plt.plot(tm, x2.value, "b-", label=r"$x_2$")
plt.plot(tm, x3.value, "r--", label=r"$x_3$")
plt.legend(loc="best")
plt.xlabel("Time")
plt.ylabel("Value")
plt.show()
I am trying to get used to Gekko as an optimization tool for some kinetic data. Therefore, I tried to implement a simple ode system based on an example of the Gekko website. Nevertheless, it never found the global maximum unless I force the derivative to be dx2/dt>0 to get the right maximum. This is somewhat unsatisfying, as I would expect a solver to find the maximum of component x2 just by asking to find the Maximum via m.Maximize(x2) for such a simple system. Is there any setting I did wrong or is my solution badly proposed? Thanks for helping.
The result: shows clearly that the maximum is not found...
The unconstrained objective (without the constraint dx2dt>=0) is 10416.8 while the objective with the constraint is 9236.4 (worse). Additional constraints never improve the optimization result but can make the objective worse for convex optimization problems such as this one.
m.Equation(x2.dt() >= 0)
Here is the code that shows the results of adding the constraint:
import numpy as np
from gekko import GEKKO
import matplotlib.pyplot as plt
m = GEKKO() # initialize gekko
nt=200
m.time = np.linspace(0,1,nt)
# Variables
x1 = m.Var(value=20,lb=0,)
x2 = m.Var(value=0,lb=0,)
x3 = m.Var(value=0,lb=0,)
p = np.zeros(nt) # mark final time point
p[-1] = 1.0
# optimize final time
tf = m.FV(value=0.7,lb=0.001,ub=100.0)
tf.STATUS = 1
# Equations
m.Equation(x1.dt() == -5*x1*tf)
m.Equation(x2.dt() == (5* x1 - 0.5*x2)*tf)
m.Equation(x3.dt() == 0.5*x2*tf)
#Minifunc
#m.Minimize(tf)
m.Maximize(x2)
#options
m.options.IMODE = 6
m.options.SOLVER = 1 #APOPT optimizer
m.options.RTOL = 1E-8
m.options.OTOL = 1E-8
m.options.NODES = 5
m.solve(disp=True,debug=1)
plt.figure(1)
tm = np.linspace(0,tf.value[0],nt)
plt.plot(tm, x2.value, "b:", label=r"$x_2$ Optimal")
# add constraint
m.Equation(x2.dt() >= 0)
m.options.TIME_SHIFT=0
m.solve(disp=True,debug=1)
tm = np.linspace(0,tf.value[0],nt)
plt.plot(tm, x2.value, "r--", label=r"$x_2$ Constrained")
plt.legend(loc="best"); plt.xlabel("Time"); plt.ylabel("Value")
print('Final Time: ' + str(tf.value[0]))
plt.figure(2) # plot results
plt.plot(tm, x1.value, "k-", label=r"$x_1$")
plt.plot(tm, x2.value, "b-", label=r"$x_2$")
plt.plot(tm, x3.value, "r--", label=r"$x_3$")
plt.legend(loc="best"); plt.xlabel("Time"); plt.ylabel("Value")
plt.show()
The current objective statement is to maximize the value of x2 everywhere.
m.Maximize(x2)
Even though there is a maximum part-way through the simulation, the final objective is the summation of all the x2 values. Make the following changes to maximize only the final x2 point.
p = np.zeros(nt) # mark final time point
p[-1] = 1.0
final = m.Param(p)
m.Maximize(x2*final)
The optimal solution is now just the final point for x2 that is 15.485.
I am trying to minimize the average root mean squared error of the following form in a class file using Gekko:
objective = np.sqrt((np.sum((ym-np.array(y))**2))/N/
(np.sum((ym-np.mean(ym))**2))/N)
Here is the code:
# Code
from math import ceil
import numpy as np
import pandas as pd
import os
from gekko import GEKKO
import sys
from demandlib.tools import add_weekdays2df
import matplotlib.pyplot as plt
class HeatBuilding_Personalized:
def __init__(self, df_index, **kwargs):
self.datapath = kwargs.get(
'datapath', os.path.join(os.path.dirname(__file__), 'bdew_data'))
self.df = pd.DataFrame(index=df_index)
self.df = add_weekdays2df(self.df, holiday_is_sunday=True,
holidays=kwargs.get('holidays'))
self.df['hour'] = self.df.index.hour + 1 # hour of the day
self.temperature = kwargs.get('temperature')
self.annual_heat_demand = kwargs.get('annual_heat_demand')
self.shlp_type = kwargs.get('shlp_type').upper()
self.wind_class = kwargs.get('wind_class')
self.building_class = kwargs.get('building_class', 0)
self.ww_incl = kwargs.get('ww_incl', True)
self.name = kwargs.get('name', self.shlp_type)
self.data_points = kwargs.get('data_points')
self.st_p= kwargs.get('st_p')
self.end= kwargs.get('end')
def get_bdew_profile(self):
""" Calculation of the normalized hourly heat demand
"""
self.df['temperature'] = self.temperature.values
self.df['temperature_geo'] = self.weighted_temperature(how='geometric_series')
sf = self.get_sf_values()
f = self.get_weekday_parameters()
# measurements
self.df['data_points']=self.data_points.values
self.df= self.df[self.st_p:self.end]
self.df=self.df.dropna()
self.annual_heat_demand= self.df['data_points'].sum()
self.temperature= pd.DataFrame(self.df['temperature'])
print(self.df)
ym = pd.DataFrame(self.df['data_points'])
print("amount of nan",str(ym.isnull().sum()))
ymeas_mean = np.mean(ym)
print(ym)
print('ymeas_mean:',ymeas_mean)
x1= np.array(self.df['temperature_geo'])
x2= np.array(self.get_weekday_parameters())
x3= np.int(self.annual_heat_demand)
x4= np.array(self.get_sf_values())
ym= np.array(ym)
# GEKKO model
m = GEKKO(remote=False)
a = m.FV( 3.7,lb=1,ub=4)
a.STATUS=1
b = m.FV(-35.1,lb=-40,ub=-30)
b.STATUS=1
c = m.FV(7.1,lb=5,ub=9)
c.STATUS=1
d = m.FV( 0.9,lb=0.1,ub=1.5)
d.STATUS=1
# variables
T_g= m.Param(value=x1)
f=m.Param(value=x2)
annual_demand=m.Param(value=x3)
sf=m.Param(value=x4)
ymeas = m.Param(value=ym)
N = len(ym)
print('index n:',N)
yest = m.CV(value=0)
yest.FSTATUS=1
# y=m.Var() # I am defining my state variabel
# y = m.Var()
# z.FSTATUS=1
# regression equation
k = m.Intermediate((a / (1 + (b / (T_g - 40)) ** c) + d))
s=m.Intermediate(np.sum(k*f))
kw=m.Intermediate( 1.0 / (s / 24))
m.Equation(yest == (k* kw * f * sf) * annual_demand)
# objectives
# m.Minimize(((yest-ymeas)/ymeas)**2)
m.Obj(m.sqrt((np.sum((ymeas-yest)**2))/N/(np.sum((ymeas-np.mean(ymeas))**2))/N))
# print('Obj init value = ' + str(object_af.value))
# regression mode
m.options.IMODE = 2
m.options.SOLVER = 1 # considering APOPT solver for 1 and IPOPT for 3
# optimize
m.options.MAX_ITER = 20
m.options.OTOL = 1.0e-10
m.options.RTOL = 1.0e-10
m.solve(disp=True)
# print parameters
# print('Obj after value = ' + str(vd.value))
print('Optimized, a = ' + str(a.value[0]))
print('Optimized, b = ' + str(b.value[0]))
print('Optimized, c = ' + str(c.value[0]))
print('Optimized, d = ' + str(d.value[0]))
# print('Optimized, h = ' + str(h.value))
# sys.exit()
print("optimization is ok")
sf = self.get_sf_values()
f = self.get_weekday_parameters()
h = (a.value[0] / (1 + (b.value[0] / (self.df['temperature_geo'] - 40)) ** c.value[0]) + d.value[0])
kw = 1.0 / (sum(h * f) / 24) #1.0 instead of annual heat demand because the #annual heat demand is already multiplied in get_bdew_profile and divide by 24 to get #daily value
y = (kw * h * f * sf) * self.annual_heat_demand
objective= np.sqrt((np.sum((ym-np.array(y))**2))/N/(np.sum((ym-np.mean(ym))**2))/N)
print('objective calculated without Gekko:',objective)
return y
It returns this output:
Outputs: Solver :
APOPT (v1.0) Solution time : 27.2771999999968 sec
Objective : 40884011.5968099
Successful solution --------------------------------------------------
Optimized, a = 3.8708321781 Optimized, b = -31.844822393
Optimized, c = 7.8648564579 Optimized, d = 1.0244814518
The objective value is high. Without Gekko the objective is calculated as 0.01904060781034217. Why is it different?
It is hard to diagnose the problem because it is missing the data to run and verify. Here are a couple things to change:
Set yest as a Variable instead of a CV. It is automatically adding squared error terms when you declare a CV with FSTATUS=1 as described in the Dynamic Optimization course and in the Estimator Tuning Lesson. Because you are defining a custom objective, there is no need to declare a CV.
#yest = m.CV(value=0)
#yest.FSTATUS=1
yest = m.Var(value=0)
For a direct comparison, try declaring Intermediate variables to inspect the parts of the objective. Also, use m.sum() instead of np.sum() for the Gekko version of summation. The den piece is a number so it can be pre-calculated before the objective function is defined with ym.
den = (np.sum((ym-np.mean(ym))**2)
m.Obj(m.sqrt((np.sum((ymeas-yest)**2))/(den*N*N)))
Please post complete minimal, verifiable code for more specific help.
so we corrected the code like said in the previous comment but we get problems when we try to take a sum of a gekko parameter and we don't understand why. s is the variable where we calculate a sum and try to use it in the next equation but it doesn't work. Even with numpy.sum() it doesn't sum it up. We get this error: TypeError: x must be a python list of GEKKO parameters, variables, or expressions
Any idea what we need to change on factor k or f so the sum can be achieved?
x1= np.array(self.df['temperature_geo'])
x2= np.array(self.get_weekday_parameters())
x3= np.int(self.annual_heat_demand)
x4= np.array(self.get_sf_values())
ym= np.array(ym)
# GEKKO model
m = GEKKO(remote=False)
# variables
T_g= m.Param(value=x1)
f=m.Param(value=x2)
annual_demand=m.Param(value=x3)
sf=m.Param(value=x4)
ymeas = m.Param(value=ym)
yest = m.Var(value=0)
# regression equation
k = m.Intermediate((a / (1 + (b / (T_g - 40)) ** c) + d))
s=m.Intermediate(m.sum(k*f))
kw=m.Intermediate( 1.0 / (s / 24))
m.Equation(yest == (k* kw * f * sf) * annual_demand)
den = (m.sum((ymeas-np.mean(ymeas))**2))
ben = m.sum((ymeas-yest)**2)
m.Obj(m.sqrt((ben)/N/(den)/N))
# print('Obj init value = ' + str(object_af.value))
# regression mode
m.options.IMODE = 2
m.options.SOLVER = 1 # considering APOPT solver for 1 and IPOPT for 3
# optimize
m.options.MAX_ITER = 20
m.options.OTOL = 1.0e-10
m.options.RTOL = 1.0e-10
m.solve(disp=True)
I'm trying to implement a receding horizon control (RHC) scheme using GEKKO in Python, and I'd like to check my formulation. The goal is to solve the OCP over some horizon from t=tk to t=tk+H-1, apply the control solution at tk, and discard the remaining values (u_k+1 to u_k+H-1). The following code appears to give the correct solution, but I want to verify I've used the correct functions in GEKKO, namely when "resetting" the states for the next horizon. I had a few issues trying to use the .VALUE function to reset x1 and x2, e.g. TypeError: 'float' object is not subscriptable.
import numpy as np
import matplotlib.pylab as plt
from gekko import GEKKO
if __name__ == '__main__':
# Instantiate GEKKO
m = GEKKO()
# Constants
nRHC = 21
tRHC = 2
m.time = np.linspace(0, tRHC, nRHC)
# Control
u = m.MV(value=0.0,fixed_initial=False)
u.STATUS = 1
u.DCOST = 0
# Vars
t = m.SV(value=0)
x1 = m.SV(value=1)
x2 = m.SV(value=0)
# Equations
m.Equation(t.dt() == 1)
m.Equation(x1.dt() == x2)
m.Equation(x2.dt() == (1 - x2*x2)*x1 - x2 + u)
# Objective Function
m.Minimize(10*x1**2 + 10*x2**2 + u**2)
# Solve RHC
m.options.IMODE = 6
m.options.NODES = 11
m.options.MV_TYPE = 2
m.options.SOLVER = 3
nTotal = 101
tTotal = np.linspace(0, 10, nTotal)
uStore = np.zeros((1,nTotal))
xStore = np.zeros((2,nTotal))
xStore[:,0] = [1, 0]
for i in range(nTotal):
print('Solving Step: ', i+1, ' of ', nTotal-1)
if i == nTotal-1:
break
# Solve MPC over horizon
m.solve(disp=False)
# Update States
t.VALUE = t[1]
x1.MEAS = x1[1]
x2.MEAS = x2[1]
# Store
uStore[:,i] = u.NEWVAL
xStore[:,i+1] = np.array([x1[1], x2[1]])
# Plot States
f1, axs = plt.subplots(2)
axs[0].plot(tTotal, xStore[0,:])
axs[0].set_ylabel('x')
axs[0].grid()
axs[1].plot(tTotal, xStore[1,:])
axs[1].set_ylabel('x_dot')
axs[1].set_xlabel('time')
axs[1].grid()
# Show Plots
plt.show()
Thank you!
There is no need to update the states because Gekko does this automatically.
# Update States
t.VALUE = t[1]
x1.MEAS = x1[1]
x2.MEAS = x2[1]
The state values are stored in run directory files (see m.path or open with m.open_folder()). The file is ctl.t0. At the next command m.solve(), that file is imported and time shifted to make the values at the next time step the initial conditions. The time shift is adjusted with m.options.TIME_SHIFT=1 (1 is the default). If you do want to override the initial condition, use x1.MEAS=x1.value[1] or x1.value=x1.value[1].
Good afernoon community, recently someone recommends me GEKKO library to do dynamic optimization through python. I'm trying to replicate this paper with that. Basically the model is:
According to the paper E is control variable and K is a state variable, the rest are constant values. I think my error is when I define E as a control variable, because when I do the output is that GEKKO can't find a solution. That's is the reason that belows I define as a parameter.
# Initialize GEKKO
m = GEKKO()
# Define values:
# Constant values
beta_1, beta_2, gamma, delta, s, alpha, L_bar = 0.2, 0.8, 0.5, 0.05, 0.4719, 10, 900000000
# Time period
n = 100
t = np.linspace(0, 15, n)
m.time = t
# Set initial conditions:
# Define K(0) = K_0 initial capital
K = m.Var(value = 0)
# Restrictions problem:
# Define constraints
import math
E = m.Param(-4 * (np.exp(t/2) - np.exp(-1))) # Control variable E
Y = m.Param((1 - gamma) * (K**beta_1) * (L_bar**(beta_2)) + gamma * E)
K_dot = m.SV(1, lb = 0, ub = 1)
m.Equation(K_dot.dt() == s * (Y - E) - delta * K)
J = m.Var(value = 0)
Jf = m.FV()
Jf.STATUS = 1
# Define objective function :
m.Connection(Jf,J,pos2='end')
m.Equation(J.dt() == -1 *(E**2) - alpha * K_dot)
# Set solving options
m.Obj(-Jf) # maximize profit
# Set solving options
m.options.IMODE = 6 # optimal control
m.options.NODES = 3 # collocation nodes
m.options.SOLVER = 3 # solver (IPOPT)
# Solve maximization problem
m.solve(disp = False) # Solve
plt.plot(t, K, "--r")
Taking a look to the documentation, I think my problem is a mix between the "load following" exmple and the "optimal control with economic objective". In order to know if I did in the right way, I use figure 5 and 6 as reference. For the figure 5 when x = 11 there is change in the graphic...but for me it isn't. While in figure 6, the y axis is from [0, -3k] and according to my script is from [0, -7k].
The decision variable E should be an MV() type and the Y variable should be an Intermediate type.
from gekko import GEKKO
import numpy as np
# Initialize GEKKO
m = GEKKO()
# Define values:
# Constant values
beta_1, beta_2 = 0.2, 0.8
gamma, delta, s, alpha, L_bar = 0.5, 0.05, 0.4719, 10, 900000000
# Time period
n = 100
t = np.linspace(0, 15, n)
m.time = t
# Set initial conditions:
# Define K(0) = K_0 initial capital
K_0 = 0.1
K = m.Var(K_0)
# Restrictions problem:
# Define constraints
E = m.MV(lb=0.1,ub=0.2); E.STATUS=1 # Variable E is adjusted to Maximize objective
Y = m.Intermediate((1-gamma) * (K**beta_1) * (L_bar**(beta_2)) + gamma*E)
m.Equation(K.dt() == s * (Y-E) - delta*K)
p = np.zeros(n); p[-1]=1; final=m.Param(p)
m.Maximize(final*m.integral(-1 *(E**2) - alpha*K)) # maximize profit
# Set solving options
m.options.IMODE = 6 # optimal control
m.options.NODES = 3 # collocation nodes
m.options.SOLVER = 1 # solver (IPOPT)
m.options.MAX_ITER=1000
# Solve maximization problem
m.solve(disp = True) # Solve
plt.plot(t, K, "--r")
plt.show()
You will need to find the correct value of K_0 from the paper and fill in any additional information such as the correct bounds on E. The solution currently appears unbounded (profit as a negative of the minimization increases with each solver iteration, up to 1000).
995 -1.11943E+06 9.76562E-04
996 -1.12056E+06 9.76562E-04
997 -1.12168E+06 9.76562E-04
998 -1.12281E+06 9.76562E-04
999 -1.12393E+06 9.76562E-04
Iter Objective Convergence
1000 -1.12506E+06 9.76562E-04
Maximum iterations
---------------------------------------------------
Solver : APOPT (v1.0)
Solution time : 11.9291999999987 sec
Objective : -1125059.40772567
Unsuccessful with error code 0
---------------------------------------------------
There are a few additional dynamic optimization problem examples that can help:
Basic Examples
More Advanced Examples
Energy Systems
The commercial fishery example may be the closest application to the one you are solving.