How to apply boundary conditions which are dependent for a system of ODE's in Gekko python? - gekko

Boundary conditions are
y1(0)=3;
y2(0)=y1(5)
In the interval of [0,5]

Here is one possible implementation in Gekko:
import numpy as np
from gekko import GEKKO
m = GEKKO(remote=False)
m.time = np.linspace(0,5)
x = m.Param(m.time)
y1 = m.Var(3)
y2 = m.Var()
m.Connection(y2,y1,'end',1,'end',1)
m.Equation(y1.dt()+2*y1+y2==m.sin(x))
m.Equation(y2.dt()-4*y1-2*y2==m.cos(x))
m.options.IMODE=6
m.solve()
However, the solver reports that there is no solution. There are some things that are unclear about your problem statement such as whether the derivative is with respect to x or t. If x is calculated, is is a single value or able to vary throughout the time horizon?
Edit in response to comment
You can implement the final conditions y2(5)=y1(5) with m.Connection(y2,y1,'end','end','end','end'). However, there is no feasible solution to this problem unless you create some type of additional degree of freedom (calculated variable). Without the final condition constraint, there is the unique solution as shown in this figure.
When you add the Connection final constraint, the solution to the differential equations cannot change and therefore the solver correctly reports too few degrees of freedom.
import numpy as np
from gekko import GEKKO
m = GEKKO(remote=False)
m.time = np.linspace(0,5)
x = m.Param(m.time)
y1 = m.Var(3)
y2 = m.Var()
m.Equation(y1.dt()+2*y1+y2==m.sin(x))
m.Equation(y2.dt()-4*y1-2*y2==m.cos(x))
m.options.IMODE=6
m.Connection(y2,y1,'end','end','end','end')
m.solve()
import matplotlib.pyplot as plt
plt.plot(x.value,y1.value,'r--',label='y1')
plt.plot(x.value,y2.value,'b-',label='y2')
plt.legend(); plt.xlabel('x'); plt.ylabel('y')
plt.show()

Related

Initializing the derivative of a state variable during process simulation

When simulating a process using GEKKO (for example, as in Example 15 here), how would I set the initial value of the derivative of a state variable? I am using IMODE=4, but I could also use IMODE=7.
[Edit] I have fitted the parameters of a ODE-model with measured input and output using IMODE=5 and I would like to predict model output beyond measured time points.
Here is a modification of Problem 8 from that same link as a simple example. To initialize the derivative, create a new variable such as dydt and define a new equation that is equal to the derivative.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO()
k = 10
m.time = np.linspace(0,20,100)
y = m.Var(value=5)
dydt = m.Var(value=0)
t = m.Param(value=m.time)
m.Equation(k*dydt==-t*y)
m.Equation(dydt==y.dt())
m.options.IMODE=4
m.solve(disp=False)
plt.plot(m.time,y.value,label='y')
plt.plot(m.time,dydt.value,label='dy/dt')
plt.xlabel('time'); plt.ylabel('y')
plt.legend(); plt.grid(); plt.show()
Unlike other differential algebraic equation (DAE) solvers, Gekko does not require consistent initial conditions for the states and derivatives. Gekko can also solve higher-index DAEs where the index is the number of times that constraints must be differentiated to return to ODE form.

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.

Step function implementation in Gekko

I am trying to implement something similar to step function in Gekko (IMODE=6).
I have tried If3 and setting custom lower and upper bounds, still no solution could be found in Gekko.
what would you recommend for such a step function or any piecewise function in Gekko?
Step functions (or any other input) are permitted in Gekko. If the step function does not depend on a condition but only on a time then you won't need the if3 function. Here is an example problem with u_step that defines the step function.
import numpy as np
from gekko import GEKKO
import matplotlib.pyplot as plt
m = GEKKO() # create GEKKO model
m.time = np.linspace(0,40,401) # time points
# create GEKKO parameter (step 0 to 2 at t=5)
u_step = np.zeros(401)
u_step[50:] = 2.0
u = m.Param(value=u_step)
# create GEKKO variables
x = m.Var(0.0)
y = m.Var(0.0)
# create GEEKO equations
m.Equation(2*x.dt()==-x+u)
m.Equation(5*y.dt()==-y+x)
# solve ODE
m.options.IMODE = 4
m.solve()
# plot results
plt.plot(m.time,u,'g:',label='u(t)')
plt.plot(m.time,x,'b-',label='x(t)')
plt.plot(m.time,y,'r--',label='y(t)')
plt.ylabel('values')
plt.xlabel('time')
plt.legend(loc='best')
plt.show()
Additional tutorials with differential equations solved with Gekko are available.

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.

Best learning algorithms concentric and not linearly separable data

Below are two scatter plots. The first one is for data points that have values of x and y, and I would like to know if there is a clustering algorithm that will automatically recognize that there are two clusters. They are concentric and not linearly separable. K-means is not right for several reasons. The other plot is similar but it has x, y and color values, and I would like to know what learning algorithm would be best at classifying or predicting the correct color from the values of x and y.
I got good classifier results for this problem using the sklearn MLPClassifier algorithm. Here is the scatter and contour plots:
Detailed code at: https://www.linkedin.com/pulse/couple-scikit-learn-classifiers-peter-thorsteinson. The simplified code below shows how it works:
import math
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix
# Generate the artificial data set and display the resulting scatter plot
x = []
y = []
z = []
for i in range(500):
rand = np.random.uniform(0.0, 2*math.pi)
randx = np.random.normal(0.0, 30.0)
randy = np.random.normal(0.0, 30.0)
if np.random.random() > 0.5:
z.append(0)
x.append(100*math.cos(rand) + randx)
y.append(100*math.sin(rand) + randy)
else:
z.append(1)
x.append(300*math.cos(rand) + randx)
y.append(300*math.sin(rand) + randy)
plt.axis('equal')
plt.axis([-500, 500, -500, 500])
plt.scatter(x, y, c=z)
plt.show()
# Run the MLPClassifier algorithm on the training data
XY = pd.DataFrame({'x': x, 'y': y})
print(XY.head())
Z = pd.DataFrame({'z': z})
print(Z.head())
XY_train, XY_test, Z_train, Z_test = train_test_split(XY, Z, test_size = 0.20)
mlp = MLPClassifier(hidden_layer_sizes=(10, 10, 10), max_iter=1000)
mlp.fit(XY_train, Z_train.values.ravel())
# Make predictions on the test data and display resulting scatter plot
predictions = mlp.predict(XY_test)
print(confusion_matrix(Z_test,predictions))
print(classification_report(Z_test,predictions))
plt.axis('equal')
plt.axis([-500, 500, -500, 500])
plt.scatter(XY_test.x, XY_test.y, c=predictions)
plt.show()

Resources