Simulation of Lorenz System crashes - ode

I only just started exploring Gekko and tried simulating the Lorenz ODE system.
Unfortunately, I get an error ("no solution found") for a simple std case the runs fine using scipy.
The problem solves fine if I only integrate up to say time=0.5 instead of 1.0
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO()
m.time = np.arange(0.0, 1.0, 0.01)
sigma = 10.; rho = 28.0; beta = 8./3.
x = m.Var(value=10); y = m.Var(value=10); z = m.Var(value=10)
t = m.Param(value=m.time)
m.Equation(x.dt()== sigma*(y - x))
m.Equation(y.dt()== x*(rho -z) - y)
m.Equation(z.dt()== x*y - beta*z)
m.options.IMODE = 4
m.options.nodes = 4
m.solve(disp=False)
plt.plot(x.value, y.value)
plt.show()

Sequential simulation m.options.IMODE=7 solves successfully. The simultaneous simulation is a larger problem (1782 Variables/Equations vs. 18 Variables/Equations). It also solves successfully if you reduce m.options.NODES=3 (1188 Variables) or increase the maximum iterations m.options.MAX_ITER=300. It failed previously because it needed 267 iterations to find the solution and the maximum iteration limit for IPOPT is 200 by default.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO()
m.time = np.arange(0.0, 1.0, 0.01)
sigma = 10.; rho = 28.0; beta = 8./3.
x = m.Var(value=10); y = m.Var(value=10); z = m.Var(value=10)
t = m.Param(value=m.time)
m.Equation(x.dt()== sigma*(y - x))
m.Equation(y.dt()== x*(rho -z) - y)
m.Equation(z.dt()== x*y - beta*z)
m.options.IMODE = 7
m.options.nodes = 4
m.solve(disp=True)
plt.plot(x.value, y.value)
plt.show()

Related

Bayesian calibration for ode system

I tried to use the 'pymc3' package in Python to calibrate a first-order ODE system in a Bayesian way.
I started with a toy ODE system first. It is dy1/dt = y2; dy2/dt = -b* y2 - c*sin(y1). b and c are the parameters I want to calibrate.
Firstly, I generated some outputs for y1 and y2 from t[0,10] by setting parameters b = 0.25 and c = 0.5 with normally distributed noise~N(0,0.7^2). Then, I calibrated the ODE system by setting prior distributions for b~N(0,1) and c~N(0,9) and sigma~HalfNormal
But it gave the errors: (1) TypeError: float() argument must be a string or a number, not 'TensorVariable'(2)ValueError: setting an array element with a sequence.
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
from scipy.integrate import ode
import pymc3 as pm
def pend(y, t, b, c):
theta, omega = y
dydt = [omega, -b*omega - c*np.sin(theta)]
return dydt
true_b = 0.25
true_c = 5.0
y0 = [np.pi - 0.1, 0.0]
t = np.linspace(0, 10, 101)
sol = odeint(pend, y0, t, args=(true_b, true_c))
true_sigma = 0.7
noise = np.random.randn(101,2)*true_sigma
Y_obs = sol+noise
pend_model = pm.Model()
with pend_model:
# Priors for unknown model parameters
b = pm.Normal('b', mu=0, sd=1)
c = pm.Normal('c', mu=7, sd=3)
sigma = pm.HalfNormal('sigma', sd=1)
# Expected value of outcome
mu = odeint(pend, y0, t, args=(b, c))
# Likelihood (sampling distribution) of observations
Y = pm.Normal('Y_obs', mu=mu, sd=sigma, observed=Y_obs)
trace = pm.sample(draws=5000, tune=500, chains=1)

Animated Polar Plot

I am trying to make an animated plot of planetary motion using python. I can get the correct path to show up when not animated but once I try to animate it, it just shows up blank even though r and i are being output as the correct values when I try to print each value in the console.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure(figsize=(6,6))
ax = plt.subplot(111, polar=True)
ax.set_ylim(0,1)
line, = ax.plot([],[])
semi_major_axis = 1
eccentricity = 0.1
theta = np.linspace(0,2*np.pi, num = 50)
point, = ax.plot(0,1, marker="o")
def frame(i):
r=(semi_major_axis*(1-eccentricity**2)/(1-eccentricity*np.cos(i)))
line.set_xdata(i)
line.set_ydata(r)
return line,
ax.set_rmax(semi_major_axis+1)
ax.set_rticks(np.linspace(0,semi_major_axis+1, num = 5))
ax.set_rlabel_position(-22.5)
animation = FuncAnimation(fig, func=frame, frames=theta, interval=10)
plt.show()
I think the problem is you are plotting a point and not a line. The following code worked for me:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure(figsize=(6, 6))
ax = plt.subplot(111, polar=True)
ax.set_ylim(0, 1)
semi_major_axis = 1
eccentricity = 0.1
theta = np.linspace(0, 2 * np.pi, num=50)
def frame(i):
r = (semi_major_axis * (1 - eccentricity ** 2) / (1 - eccentricity * np.cos(i)))
ax.plot(i,r,'b', marker='o')
ax.set_rmax(semi_major_axis + 1)
ax.set_rticks(np.linspace(0, semi_major_axis + 1, num=5))
ax.set_rlabel_position(-22.5)
animation = FuncAnimation(fig, func=frame, frames=theta, interval=10)
plt.show()

Optimization integer programming with covariance matrix

I am trying to do a optimization problem which requires the calculation of a new covariance matrix affected by the variable within the implementation.
I am able to do so with scipy optimization Minimize using numpy.cov within my objective function. However, as I need to have integer constraints, I am not able to think of a solution which tackles my issue with cvxpy, gekko since most of the optimization problem online have a fixed covariance matrix.
Below is my code for scipy:
room_revpar = np.array(df.iloc[:,1:10])
nla = np.array([753.2,1077.6, 1278.6,1463.9,1657.0,1990.6,2404.9,2754.6,3464.72])
min_nla = 270517.16
max_nla = 271270.359995
def objective(x, room_revpar,nla,sign = -1.0):
room_revenue = room_revpar * x
avg_revenue = np.mean(room_revenue, axis = 0)
total_revenue = sum(avg_revenue)
cov_matrix = np.cov(room_revenue.T)
total_nla = np.matmul(x.T, nla)
weights = x * nla / total_nla
portfolio_sd = np.sqrt(np.matmul(np.matmul(weights.T, cov_matrix), weights))
adj_risk = total_revenue / portfolio_sd
return sign * adj_risk
def constraint1(x, nla, min_nla):
total_nla = np.matmul(x.T, nla)
return total_nla - min_nla
def constraint2(x, nla, max_nla):
total_nla = np.matmul(x.T, nla)
return max_nla - total_nla
con1 = {'type': 'ineq', 'fun': constraint1, 'args': (nla, min_nla)}
con2 = {'type': 'ineq', 'fun': constraint2, 'args': (nla, max_nla)}
from scipy.optimize import minimize
x = np.ones(9)
sol = minimize(objective,x0 = x, args = (room_revpar, nla), constraints = (con1,con2), options = {'maxiter': 100000})
Would appreciate if anybody has a solution! Thank you.
The covariance of xi and yi is calculated explicitly with np.cov().
import numpy as np
xi = [2.1,2.5,3.6,4.0]
yi = [8,10,12,14]
print(np.cov(xi,yi))
The function np.cov(xi,yi) returns a 2x2 symmetric matrix
[[cov[xi,xi],cov[xi,yi]],
[cov[xi,yi],cov[yi,yi]]]
[[0.80333333 2.26666667]
[2.26666667 6.66666667]]
Gekko needs a symbolic form of the covariance formula for the gradient-based optimizer. Below is a function cov() that creates the symbolic covariance calculation with Gekko variables.
import numpy as np
from gekko import GEKKO
def cov(m,x,y,ddof=1):
''' Calculate the covariance matrix of x, y
Inputs:
m: Gekko model
x: x vector of equal length to y
y: y vector of equal length to x
[ddof=1]: delta degrees of freedom
Returns:
c: covariance as a Gekko variable
'''
nx = len(x); ny = len(y) # length of x, y
if nx!=ny:
print('Error: mismatch of x and y')
xm = m.sum(x)/nx # mean of x
ym = m.sum(y)/ny # mean of y
c = m.Var() # covariance
m.Equation(c==(m.sum([(x[i]-xm)*(y[i]-ym) \
for i in range(nx)]))/(nx-ddof))
return c
m = GEKKO()
n = 4
x = m.Array(m.Param,n)
y = m.Array(m.Param,n)
xi = [2.1,2.5,3.6,4.0]
yi = [8,10,12,14]
for i in range(n):
x[i].value = xi[i]
y[i].value = yi[i]
c0 = cov(m,x,y,ddof=0)
c1 = cov(m,x,y)
m.solve(disp=False)
print('Covariance (Numpy) population cov: ', np.cov(xi,yi,ddof=0)[0,1])
print('Covariance (Numpy) sample cov: ', np.cov(xi,yi)[0,1])
print('Covariance (Gekko) population cov: ', c0.value[0])
print('Covariance (Gekko) sample cov: ', c1.value[0])
Gekko and Numpy produce the same results for the fixed xi and yi values:
Covariance (Numpy) population cov: 1.7
Covariance (Numpy) sample cov: 2.2666666666666666
Covariance (Gekko) population cov: 1.7
Covariance (Gekko) sample cov: 2.2666666667
Now that the cov() function is verified, you can switch x and y to be calculated integer values such as:
x = m.Array(m.Var,n,lb=0,ub=10,integer=True)
y = m.Array(m.Var,n,lb=0,ub=5,integer=True)
To obtain an integer solution, switch to m.options.SOLVER=1 (APOPT) solver before the m.solve() command.

Matplotlib Countour not Connected

As a Python novice and trying to visualize the curve X2*Y + X*Y2 - X4 - Y4 = 0 with Matplotlib:
from matplotlib.pyplot import *
from sympy import *
from numpy import *
delta = 0.025
p = arange(-0.5, 1.5, delta)
q = arange(-0.5, 1.5, delta)
X, Y = meshgrid(p, q)
Z = X**2*Y + X*Y**2 - X**4 - Y**4
fig, ax = subplots()
CS = ax.contour(X, Y, Z, [0], colors ='k')
ax.set_title('x**2*y + x*y**2 - x**4 - y**4')
show()
the result is that the plot is not connected, whereas mathematically, it should be so. How can the level set be connected?
It's a year later, but for future reference: You just have to choose a smaller stepsize delta. With your delta = 0.025 your get the disconnected picture:
With delta = 0.001 you get a more accurate connected picture:

How can i use gekko python to control the level of a tank of a cstr by manipulating the inlet flow to the tank?- Part 2

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. in addition, I tried putting qout=5.0, Ac=30 and h0=50, it also didn't work.
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=10.0
h_ss=50.0
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]
Ac=30.0
# initial conditions
h0=50.0
q0=10.0
m.q=m.MV(value=q0,lb=0,ub=100)
m.h= m.CV(value=h0)
m.qout=m.Param(value=5)
m.Equation(m.h.dt()==(m.q- m.qout)/Ac)
#MV tuning
m.q.STATUS = 1
m.q.FSTATUS = 0
m.q.DMAX = 100
m.q.DMAXHI = 5
m.q.DMAXLO = -100
#CV tuning
m.h.STATUS = 1
m.h.FSTATUS = 1
m.h.TR_INIT = 2
m.h.TAU = 1.0
m.h.SP = 55.0
m.options.CV_TYPE = 2
m.options.IMODE = 6
m.options.SOLVER = 3
#%% define CSTR model
def cstr(x,t,u2,Ac):
q=u2
Ac=30.0
# States (2):
# the height of the tank (m)
h=x[0]
# Parameters:
# Calculate height derivative
dhdt=(q-5.0)/Ac
# Return xdot:
xdot = np.zeros(1)
xdot[0]= dhdt
return xdot
# Time Interval (min)
t = np.linspace(0,6,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] = 55.0
hsp[100:150]=70.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]
m.h.SP=hsp[i+1]
# solve MPC
m.solve(disp=True)
# 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)
The problem is with your input to the simulator y = odeint(cstr,x0,ts,args=(u2[i+1],Ac)). It should be using the value from the prior loop: y = odeint(cstr,x0,ts,args=(u2[i],Ac)). Here is an updated script.
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
from gekko import GEKKO
# Steady State Initial Condition
u2_ss=10.0
h_ss=50.0
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]
Ac=30.0
# initial conditions
h0=50.0
q0=10.0
m.q=m.MV(value=q0,lb=0,ub=100)
m.h= m.CV(value=h0)
m.qout=m.Param(value=5)
m.Equation(Ac * m.h.dt()==m.q- m.qout)
#MV tuning
m.q.STATUS = 1
m.q.FSTATUS = 0
m.q.DMAX = 100
m.q.DMAXHI = 5
m.q.DMAXLO = -100
#CV tuning
m.h.STATUS = 1
m.h.FSTATUS = 1
m.h.TR_INIT = 2
m.h.TAU = 1.0
m.h.SP = 55.0
m.options.CV_TYPE = 2
m.options.IMODE = 6
m.options.SOLVER = 3
#%% define CSTR model
def cstr(x,t,u2,Ac):
q=u2
Ac=30.0
# States (2):
# the height of the tank (m)
h=x[0]
# Parameters:
# Calculate height derivative
dhdt=(q-5)/Ac
# Return xdot:
xdot = np.zeros(1)
xdot[0]= dhdt
return xdot
# Time Interval (min)
t = np.linspace(0,6,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] = 55.0
hsp[100:150]=70.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],Ac))
# retrieve measurements
h[i+1]= y[-1][0]
# insert measurement
m.h.MEAS=h[i+1]
m.h.SP=hsp[i+1]
# solve MPC
m.solve(disp=False)
# retrieve new q value
u2[i+1] = m.q.NEWVAL
# update initial conditions
x0[0]= h[i+1]
#%% Plot the results
if i%10==0:
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)

Resources