I am doing numerical optimization using Gekko in for loop. In the loop I am reading an element of array to optimize the objective function with constraint against that particular element. However, optimization stops after some iteration and gives following error: Exception: #error: Solution Not Found. I then pass the same element to optimzation routine but without loop and for the same element it gives me a solution.
There is no problem to use Gekko in a loop. Here is an example with a single-threaded process.
Gekko in Optimization Loop
import numpy as np
from gekko import GEKKO
# Optimize at mesh points
x = np.arange(20.0, 30.0, 2.0)
y = np.arange(30.0, 50.0, 4.0)
amg, bmg = np.meshgrid(x, y)
# Initialize results array
obj = np.empty_like(amg)
m = GEKKO(remote=False)
objective = float('NaN')
a,b = m.Array(m.FV,2)
# model variables, equations, objective
x1 = m.Var(1,lb=1,ub=5)
x2 = m.Var(5,lb=1,ub=5)
x3 = m.Var(5,lb=1,ub=5)
x4 = m.Var(1,lb=1,ub=5)
m.Equation(x1*x2*x3*x4>=a)
m.Equation(x1**2+x2**2+x3**2+x4**2==b)
m.Minimize(x1*x4*(x1+x2+x3)+x3)
m.options.SOLVER = 1 # APOPT solver
# Calculate obj at all meshgrid points
for i in range(amg.shape[0]):
for j in range(bmg.shape[1]):
a.MEAS = amg[i,j]
b.MEAS = bmg[i,j]
m.solve(disp=False)
obj[i,j] = m.options.OBJFCNVAL
print(amg[i,j],bmg[i,j],obj[i,j])
# plot 3D figure of results
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(amg, bmg, obj, \
rstride=1, cstride=1, cmap=cm.coolwarm, \
vmin = 10, vmax = 25, linewidth=0, antialiased=False)
ax.set_xlabel('a')
ax.set_ylabel('b')
ax.set_zlabel('obj')
plt.show()
Gekko also supports multi-threading as shown with the same example with the threading package.
Multi-Threaded Gekko Optimization
import numpy as np
import threading
import time, random
from gekko import GEKKO
class ThreadClass(threading.Thread):
def __init__(self, id, server, ai, bi):
s = self
s.id = id
s.server = server
s.m = GEKKO()
s.a = ai
s.b = bi
s.objective = float('NaN')
# initialize variables
s.m.x1 = s.m.Var(1,lb=1,ub=5)
s.m.x2 = s.m.Var(5,lb=1,ub=5)
s.m.x3 = s.m.Var(5,lb=1,ub=5)
s.m.x4 = s.m.Var(1,lb=1,ub=5)
# Equations
s.m.Equation(s.m.x1*s.m.x2*s.m.x3*s.m.x4>=s.a)
s.m.Equation(s.m.x1**2+s.m.x2**2+s.m.x3**2+s.m.x4**2==s.b)
# Objective
s.m.Minimize(s.m.x1*s.m.x4*(s.m.x1+s.m.x2+s.m.x3)+s.m.x3)
# Set global options
s.m.options.IMODE = 3 # steady state optimization
s.m.options.SOLVER = 1 # APOPT solver
threading.Thread.__init__(s)
def run(self):
# Don't overload server by executing all scripts at once
sleep_time = random.random()
time.sleep(sleep_time)
print('Running application ' + str(self.id) + '\n')
# Solve
self.m.solve(disp=False)
# Results
#print('')
#print('Results')
#print('x1: ' + str(self.m.x1.value))
#print('x2: ' + str(self.m.x2.value))
#print('x3: ' + str(self.m.x3.value))
#print('x4: ' + str(self.m.x4.value))
# Retrieve objective if successful
if (self.m.options.APPSTATUS==1):
self.objective = self.m.options.objfcnval
else:
self.objective = float('NaN')
self.m.cleanup()
# Select server
server = 'https://byu.apmonitor.com'
# Optimize at mesh points
x = np.arange(20.0, 30.0, 2.0)
y = np.arange(30.0, 50.0, 2.0)
a, b = np.meshgrid(x, y)
# Array of threads
threads = []
# Calculate objective at all meshgrid points
# Load applications
id = 0
for i in range(a.shape[0]):
for j in range(b.shape[1]):
# Create new thread
threads.append(ThreadClass(id, server, a[i,j], b[i,j]))
# Increment ID
id += 1
# Run applications simultaneously as multiple threads
# Max number of threads to run at once
max_threads = 8
for t in threads:
while (threading.activeCount()>max_threads):
# check for additional threads every 0.01 sec
time.sleep(0.01)
# start the thread
t.start()
# Check for completion
mt = 3.0 # max time
it = 0.0 # incrementing time
st = 1.0 # sleep time
while (threading.activeCount()>=1):
time.sleep(st)
it = it + st
print('Active Threads: ' + str(threading.activeCount()))
# Terminate after max time
if (it>=mt):
break
# Wait for all threads to complete
#for t in threads:
# t.join()
#print('Threads complete')
# Initialize array for objective
obj = np.empty_like(a)
# Retrieve objective results
id = 0
for i in range(a.shape[0]):
for j in range(b.shape[1]):
obj[i,j] = threads[id].objective
id += 1
# plot 3D figure of results
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(a, b, obj, \
rstride=1, cstride=1, cmap=cm.coolwarm, \
vmin = 12, vmax = 22, linewidth=0, antialiased=False)
ax.set_xlabel('a')
ax.set_ylabel('b')
ax.set_zlabel('obj')
ax.set_title('Multi-Threaded GEKKO')
plt.show()
Related
my optimization model works but with different initial values for the main variable (Pre), it gives a different answer! not the optimal one! but this should have one answer.
I do not understand why!
try:
from pip import main as pipmain
except:
from pip._internal import main as pipmain
pipmain(['install','gekko'])
from gekko import GEKKO
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#Initialize Model
m = GEKKO(remote=False)
#define parameter
df=pd.read_excel (r'C:\Users\....')
Pw=pd.DataFrame(df).values
eta = m.Const(value=0.6)
Pre=m.Var(lb=20, ub=30)
Pre.value=23
def f(Pw,Pre):
Dplus=m.Var(value=0)
Dminus=m.Var(value=0)
for i in range(744):
D=float(Pw[i])-Pre.value
if D>=0:
Dplus.value=Dplus.value+D*eta.value
elif D<0:
Dminus.value=Dminus.value+D
return Dplus+Dminus
#constraint:
m.Equation(f(Pw,Pre)>=0)
#Objective:
m.Minimize(f(Pw,Pre))
#Set global options:
m.options.IMODE = 2 #steady state optimization
#Solve simulation:
m.solve()
You shouldn't use .value to build model equations because it only references the initial guess value, not the variable value. Use the m.if3() (preferred) or m.if2() functions to use conditional statements in the model. Here is an example of m.if3():
import numpy as np
import matplotlib.pyplot as plt
from gekko import GEKKO
m = GEKKO(remote=False)
p = m.Param()
y = m.if3(p-4,p**2,p+1)
# solve with condition<0
p.value = 3
m.solve(disp=False)
print(y.value)
# solve with condition>=0
p.value = 5
m.solve(disp=False)
print(y.value)
An even better way to incorporate conditional statements is to use slack variables. It appears that your application is for energy storage where there is inefficiency in the storage process given by eta. Here is a simple energy storage problem (see problem 4) where the loss from storage and retrieval is embedded in the optimization.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO(remote=False)
m.time = np.linspace(0,1,101)
g = m.FV(); g.STATUS = 1 # production
s = m.Var(1e-2, lb=0) # storage inventory
store = m.Var() # store energy rate
s_in = m.Var(lb=0) # store slack variable
recover = m.Var() # recover energy rate
s_out = m.Var(lb=0) # recover slack variable
eta = 0.7
d = m.Param(-2*np.sin(2*np.pi*m.time)+10)
m.periodic(s)
m.Equations([g + recover/eta - store >= d,
g - d == s_out - s_in,
store == g - d + s_in,
recover == d - g + s_out,
s.dt() == store - recover/eta,
store * recover <= 0])
m.Minimize(g)
m.options.SOLVER = 1
m.options.IMODE = 6
m.options.NODES = 3
m.solve()
plt.figure(figsize=(6,3))
plt.subplot(2,1,1)
plt.plot(m.time,d,'r-',label='Demand')
plt.plot(m.time,g,'b:',label='Prod')
plt.legend(); plt.grid(); plt.xlim([0,1])
plt.subplot(2,1,2)
plt.plot(m.time,s,'k-',label='Storage')
plt.plot(m.time,store,'g--', label='Store Rate')
plt.plot(m.time,recover,'b:', label='Recover Rate')
plt.legend(); plt.grid(); plt.xlim([0,1])
plt.show()
I'm having some computational problems with the following code:
import numpy as np
from numpy import arange
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from scipy.integrate import quad
import matplotlib as mpl
mpl.rcParams['agg.path.chunksize'] = 10000
# parameters
Ms = 100 #GeV Singlet Mass
Me = 0.511e-3 #Gev Electron Mass
Mp = 1.22e19 #GeV Planck Mass
gs = 106.75 # Entropy dof
H0 = 2.133*(0.7)*1e-42 # GeV Hubble parameter (unused)
gx = 2 # WIMP's dof
g = 100 # total dof
sigmav=[1e-25,1e-11,1e-12] # cross section's order of magnitude
xi=1e-2
xe=1e2
npts=int(1e5)
x = np.linspace(xi, xe, npts)
def fMB(p,x,m):
return np.exp(-x*np.sqrt(1+p*p/(m*m)))*p*p
def neq(x,m):
return (gx/(2*np.pi*np.pi))*quad(fMB, 0, np.inf, args=(x,m))[0]
def neq_nr(x,m):
return 2*(m**2/(2*np.pi*x))**(3/2)*np.exp(-x)
def stot(x):
return (2*np.pi*np.pi/45)*gs*Ms*Ms*Ms/(x*x*x)
def Yeq(x,m):
return neq(x,m)/stot(x)
Yeq2=np.vectorize(Yeq)
def Yeq_nr(x):
return 0.145*(gx/gs)*(x)**(3/2)*np.exp(-x)
def Yeq_r(x):
return 0.278*(3*gx/4)/gs
def Ytot(x):
if np.any(x<=1):
return Yeq_r(x)
else:
return Yeq_nr(x)
def eqd(yl,x,Ms,σv):
'''
Ms [GeV] : Singlet Mass
σv: [1/GeV^2] : ⟨σv⟩
'''
H = 1.67*g**(1/2)*Ms**2/Mp
dyl = -neq(x,Ms)*σv*(yl**2-Yeq(x,Ms)**2)/(x**(-2)*H*x*Yeq(x,Ms)) #occorre ancora dividere per Yeq_nr(x) oppure Yeq(x)
return dyl
y0=1e-15
yl0 = odeint( eqd, y0, x,args=(Ms,sigmav[0]), full_output=True)
yl1 = odeint( eqd, y0, x,args=(Ms,sigmav[1]), full_output=True)
yl2 = odeint( eqd, y0, x,args=(Ms,sigmav[2]), full_output=True)
fig = plt.figure(figsize=(11,8))
plt.loglog(x,yl0[0], label = r'$\langle σ v\rangle = %s {\rm GeV}^{-2}$'%(sigmav[0]))
plt.loglog(x,yl1[0], label = r'$\langle σ v\rangle = %s {\rm GeV}^{-2}$'%(sigmav[1]))
plt.loglog(x,yl2[0], label = r'$\langle σ v\rangle = %s {\rm GeV}^{-2}$'%(sigmav[2]))
plt.loglog(x,Yeq_nr(x), '--', label = '$Y_{EQ}^{nr}$')
plt.loglog(x,Yeq2(x,Ms), '--', label = '$Y_{EQ}$')
plt.ylim(ymax=0.1,ymin=y0)
plt.xlim(xmax=xe,xmin=xi)
plt.xlabel('$x = m_χ/T$', size= 15)
plt.ylabel('$Y$', size= 15)
plt.title('$m_χ = %s$ GeV'%(Ms), size= 15)
plt.legend(loc='best',fontsize=12)
plt.grid(True)
plt.savefig('abundance.jpg',bbox_inches='tight', dpi=150)
In particular, as soon as I use little values of sigmav (ranging from 10^-12 to 10^-25) the solution is well displayed, but making use of bigger values (starting from 10^-11) I obtain problems and I guess is a order of magnitudes problem, but I don't know how to handle it!
Thanks to everyone!
Edit 1:
I'm uploading a plot making use of three different values of sigmav and as you may see the bigger one (1e-10) is showing (I guess) precision problems plot_1
Could not find an implementation of a multi-start approach for the solution of nlp optimization problems with GEKKO. Here there is an example using the six-hump function as a case study. The six-hump function is difficult to optimize due to the presence of multiple local optima. The multi-start approach works well and increases the chances to solve optimisation problems globally when combined with robust derivative based solvers as the ones included in GEKKO.
import numpy as np
from gekko import GEKKO
import sobol_seq
# General definition of the problem
lb = np.array([-3.0, -2.0]) # lower bounds
ub = np.array([3.0, 2.0]) # upper bounds
n_dim = lb.shape[0] # number of dimensions
# matrix of initial values
multi_start = 10 # number of times the optimisation problem is to be solved
# Sobol
sobol_space = sobol_seq.i4_sobol_generate(n_dim, multi_start)
x_initial = lb + (ub-lb)*sobol_space # array containing the initial points
# Multi-start optimisation
localsol = [0]*multi_start # storage of local solutions
localval = np.zeros((multi_start))
for i in range(multi_start):
print('multi-start optimization, iteration =', i+1)
# definition of the problem class with GEKKO
m = GEKKO(remote=False)
m.options.SOLVER = 3
x = m.Array(m.Var, n_dim)
# definition of the initial values and bounds for the optimizer
for j in range(n_dim):
x[j].value = x_initial[i,j]
x[j].lower = lb[j]
x[j].upper = ub[j]
# Definition of the objective function
f = (4 - 2.1*x[0]**2 + (x[0]**4)/3)*x[0]**2 + x[0]*x[1] \
+ (-4 + 4*x[1]**2)*x[1]**2 # six-hump function
# Solving the problem
m.Obj(f)
m.solve(disp=False)
localval[i] = m.options.OBJFCNVAL
x_local = [x[j].value for j in range(n_dim)]
localsol[i] = np.array(x_local)
# selecting the best solution
minindex = np.argmin(localval)
x_opt = localsol[minindex]
f_opt = localval[minindex]
Thanks for posting the multi-start code. That is an interesting problem with a simple objective function. A contour plot shows the six local minima of the objective function.
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
# Variables at mesh points
x = np.arange(-3, 3, 0.05)
y = np.arange(-2, 2, 0.05)
X,Y = np.meshgrid(x, y)
obj=(4-2.1*X**2+(X**4)/3)*X**2+X*Y \
+(-4+4*Y**2)*Y**2 # six-hump function
# Create a contour plot
plt.figure()
# Weight contours
CS = plt.contour(X, Y, obj,np.linspace(-1,100,102))
plt.clabel(CS, inline=1, fontsize=8)
plt.xlabel('X')
plt.ylabel('Y')
plt.show()
Gekko can run in parallel with multiple threads when searching for a global solution. The optimal solution is the orange dot.
import numpy as np
import threading
import time, random
from gekko import GEKKO
import sobol_seq
class ThreadClass(threading.Thread):
def __init__(self, id, xi, yi):
s = self
s.id = id
s.m = GEKKO(remote=False)
s.x = xi
s.y = yi
s.objective = float('NaN')
# initialize variables
s.m.x = s.m.Var(xi,lb=-3,ub=3)
s.m.y = s.m.Var(yi,lb=-2,ub=2)
# Objective
s.m.Minimize((4-2.1*s.m.x**2+(s.m.x**4)/3)*s.m.x**2+s.m.x*s.m.y \
+ (-4+4*s.m.y**2)*s.m.y**2)
# Set global options
s.m.options.SOLVER = 3 # APOPT solver
threading.Thread.__init__(s)
def run(self):
# Don't overload server by executing all scripts at once
sleep_time = random.random()
time.sleep(sleep_time)
print('Running application ' + str(self.id) + '\n')
# Solve
self.m.solve(disp=False)
# Retrieve objective if successful
if (self.m.options.APPSTATUS==1):
self.objective = self.m.options.objfcnval
else:
self.objective = float('NaN')
self.m.cleanup()
# General definition of the problem
lb = np.array([-3.0, -2.0]) # lower bounds
ub = np.array([3.0, 2.0]) # upper bounds
n_dim = lb.shape[0] # number of dimensions
# matrix of initial values
multi_start = 10 # number of times the optimisation problem is to be solved
# Sobol
sobol_space = sobol_seq.i4_sobol_generate(n_dim, multi_start)
x_initial = lb + (ub-lb)*sobol_space # array containing the initial points
# Array of threads
threads = []
# Calculate objective for each initial value
id = 0
for i in range(multi_start):
# Create new thread
threads.append(ThreadClass(id, x_initial[i,0], x_initial[i,1]))
# Increment ID
id += 1
# Run applications simultaneously as multiple threads
# Max number of threads to run at once
max_threads = 8
for t in threads:
while (threading.activeCount()>max_threads):
# check for additional threads every 0.01 sec
time.sleep(0.01)
# start the thread
t.start()
# Check for completion
mt = 3.0 # max time
it = 0.0 # incrementing time
st = 1.0 # sleep time
while (threading.activeCount()>=1):
time.sleep(st)
it = it + st
print('Active Threads: ' + str(threading.activeCount()))
# Terminate after max time
if (it>=mt):
break
# Wait for all threads to complete
#for t in threads:
# t.join()
#print('Threads complete')
# Initialize array for objective
obj = np.empty(multi_start)
xs = np.empty_like(obj)
ys = np.empty_like(obj)
# Retrieve objective results
id = 0
for i in range(multi_start):
xs[i] = threads[id].m.x.value[0]
ys[i] = threads[id].m.y.value[0]
obj[i] = threads[id].objective
id += 1
print('Objective',obj)
print('x',xs)
print('y',ys)
best = np.argmin(obj)
print('Lowest Objective',best)
print('Best Objective',obj[best])
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
# Variables at mesh points
x = np.arange(-3, 3, 0.05)
y = np.arange(-2, 2, 0.05)
X,Y = np.meshgrid(x, y)
obj=(4-2.1*X**2+(X**4)/3)*X**2+X*Y \
+(-4+4*Y**2)*Y**2 # six-hump function
# Create a contour plot
plt.figure()
# Weight contours
CS = plt.contour(X, Y, obj,np.linspace(-1,100,102))
plt.plot(xs,ys,'rx')
plt.plot(xs[best],ys[best],color='orange',marker='o',alpha=0.7)
plt.clabel(CS, inline=1, fontsize=8)
plt.xlabel('X')
plt.ylabel('Y')
plt.show()
In additional to finding all six local optimal values, one of the solutions is at a local maximum at (0,0). This can happen when local solvers are tasked with solving a global optimum. Here is the solution:
Objective [-1.80886276e-35 -2.15463824e-01 -2.15463824e-01 -2.15463824e-01
2.10425031e+00 -1.03162845e+00 -2.15463824e-01 2.10425031e+00
-1.03162845e+00 -2.15463824e-01]
x [-1.32794585e-19 1.70360672e+00 -1.70360672e+00 1.70360672e+00
1.60710475e+00 8.98420119e-02 -1.70360672e+00 -1.60710477e+00
-8.98420136e-02 1.70360671e+00]
y [ 2.11414394e-18 -7.96083565e-01 7.96083565e-01 -7.96083569e-01
5.68651455e-01 -7.12656403e-01 7.96083569e-01 -5.68651452e-01
7.12656407e-01 -7.96083568e-01]
Lowest Objective 5
Best Objective -1.0316284535
I'm trying to use GEKKO to fit to a certain dataset, using the FOPDT Optimization Method to estimate k, tau and theta.
I saw the example using odeint on https://apmonitor.com/pdc/index.php/Main/FirstOrderOptimization and tried to do the same thing with GEKKO, but I can't use the value of theta in the equation.
I saw this question where should the delay call be placed inside a gekko code? and the docs https://apmonitor.com/wiki/index.php/Apps/TimeDelay, but in this case I wanted to estimate the value of theta and not use the initial guess value. I tried to use gekko's delay, but I get an error that it only works if the delay is an int value (and not a gekko FV).
I also tried to use time directly in the equation, but I can't figure out how to place x(t-theta) in there, since I can't do that syntax with gekko variables.
import pandas as pd
import numpy as np
from gekko import GEKKO
import plotly.express as px
data = pd.read_csv('data.csv',sep=',',header=0,index_col=0)
xm1 = data['x']
ym1 = data['y']
xm = xm1.to_numpy()
ym = ym1.to_numpy()
xm_r = len(xm)
tm = np.linspace(0,xm_r-1,xm_r)
m = GEKKO()
m.options.IMODE=5
m.time = tm
k = m.FV()
k.STATUS=1
tau = m.FV()
tau.STATUS=1
theta = m.FV()
theta.STATUS=1
x = m.Param(value=xm)
y = m.CV()
y.FSTATUS = 1
yObj = m.Param(value=ym)
xtheta = m.Var()
m.delay(x,xtheta,theta)
m.Equation(y.dt()==(-y + k * xtheta)/tau)
m.Minimize((y-yObj)**2)
m.options.EV_TYPE=2
m.solve(disp=True)
Here are some strategies for implementing variable time-delay in a model such as when an optimizer adjusts the time delay in a First Order Plus Dead Time (FOPDT) model.
Create a cubic spline (continuous approximation) of the relationship between time t and the input u. This allows a fractional time delay that is not restricted to an integer multiple of the sample interval.
Create time as a variable with derivative equal to 1.
Define tc with an equation tc==time-theta to get the time shifted value. This will lookup the spline uc value that corresponds to this tc value.
You can also fit the FOPDT model to data with Excel or other tools.
from gekko import GEKKO
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# load data
url = 'http://apmonitor.com/do/uploads/Main/tclab_siso_data.txt'
data = pd.read_csv(url)
t = data['time'].values
u = data['voltage'].values
y = data['temperature'].values
m = GEKKO(remote=False)
m.time = t; time = m.Var(0); m.Equation(time.dt()==1)
K = m.FV(lb=0,ub=1); K.STATUS=1
tau = m.FV(lb=1,ub=300); tau.STATUS=1
theta = m.FV(lb=2,ub=30); theta.STATUS=1
# create cubic spline with t versus u
uc = m.Var(u); tc = m.Var(t); m.Equation(tc==time-theta)
m.cspline(tc,uc,t,u,bound_x=False)
ym = m.Param(y)
yp = m.Var(y); m.Equation(tau*yp.dt()+(yp-y[0])==K*(uc-u[0]))
m.Minimize((yp-ym)**2)
m.options.IMODE=5
m.solve()
print('K: ', K.value[0])
print('tau: ', tau.value[0])
print('theta: ', theta.value[0])
plt.figure()
plt.subplot(2,1,1)
plt.plot(t,u)
plt.legend([r'$V_1$ (mV)'])
plt.ylabel('MV Voltage (mV)')
plt.subplot(2,1,2)
plt.plot(t,y)
plt.plot(t,yp)
plt.legend([r'$T_{1meas}$',r'$T_{1pred}$'])
plt.ylabel('CV Temp (degF)')
plt.xlabel('Time')
plt.savefig('sysid.png')
plt.show()
K: 0.25489655932
tau: 229.06377617
theta: 2.0
Another way to approach this is to estimate a higher-order ARX model and then determine the statistical significance of the beta terms. Here is an example of using the Gekko sysid function.
from gekko import GEKKO
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# load data and parse into columns
url = 'http://apmonitor.com/do/uploads/Main/tclab_siso_data.txt'
data = pd.read_csv(url)
t = data['time']
u = data['voltage']
y = data['temperature']
# generate time-series model
m = GEKKO()
# system identification
na = 5 # output coefficients
nb = 5 # input coefficients
yp,p,K = m.sysid(t,u,y,na,nb,pred='meas')
print('alpha: ', p['a'])
print('beta: ', p['b'])
print('gamma: ', p['c'])
plt.figure()
plt.subplot(2,1,1)
plt.plot(t,u)
plt.legend([r'$V_1$ (mV)'])
plt.ylabel('MV Voltage (mV)')
plt.subplot(2,1,2)
plt.plot(t,y)
plt.plot(t,yp)
plt.legend([r'$T_{1meas}$',r'$T_{1pred}$'])
plt.ylabel('CV Temp (degF)')
plt.xlabel('Time')
plt.savefig('sysid.png')
plt.show()
With results:
alpha: [[0.525143 ]
[0.19284469]
[0.08177381]
[0.06152181]
[0.12918898]]
beta: [[[-8.51804876e-05]
[ 5.88425202e-04]
[ 1.99205676e-03]
[-2.81456773e-03]
[ 2.38110003e-03]]]
gamma: [0.75189199]
The first two beta terms are nearly zero but they can also be left in the model for a higher-order representation of the system.
I'm trying to solve the Lorenz system using the 4th order Runge Kutta method, where
dx/dt=a*(y-x)
dy/dt=x(b-z)-y
dx/dt=x*y-c*z
Since this system doesn't depend explicity on time, it's possibly to ignore that part in the iteration, so I just have
dX=F(x,y,z)
def func(x0):
a=10
b=38.63
c=8/3
fx=a*(x0[1]-x0[0])
fy=x0[0]*(b-x0[2])-x0[1]
fz=x0[0]*x0[1]-c*x0[2]
return np.array([fx,fy,fz])
def kcontants(f,h,x0):
k0=h*f(x0)
k1=h*f(f(x0)+k0/2)
k2=h*f(f(x0)+k1/2)
k3=h*f(f(x0)+k2)
#note returned K is a matrix
return np.array([k0,k1,k2,k3])
x0=np.array([-8,8,27])
h=0.001
t=np.arange(0,50,h)
result=np.zeros([len(t),3])
for time in range(len(t)):
if time==0:
k=kcontants(func,h,x0)
result[time]=func(x0)+(1/6)*(k[0]+2*k[1]+2*k[2]+k[3])
else:
k=kcontants(func,h,result[time-1])
result[time]=result[time-1]+(1/6)*(k[0]+2*k[1]+2*k[2]+k[3])
The result should be the Lorenz atractors, however my code diverges around the fifth iteration, and it's because the contants I create in kconstants do, however I checked and I'm pretty sure the runge kutta impletmentation is not to fault... (at least i think)
edit:
Found a similar post ,yet can't figure what I'm doing wrong
You have an extra call of f(x0) in the calculation of k1, k2 and k3. Change the function kcontants to
def kcontants(f,h,x0):
k0=h*f(x0)
k1=h*f(x0 + k0/2)
k2=h*f(x0 + k1/2)
k3=h*f(x0 + k2)
#note returned K is a matrix
return np.array([k0,k1,k2,k3])
Have you looked at different initial values for your calculation? Do the ones you've chosen make sense? I.e. are they physical? From past experience with rk you can sometimes get very confusing results if you pick silly starting parameters.
Goodnight. This and version I made using the scipy edo integrator, scipy.integrate.odeint.
# Author : Carlos Eduardo da Silva Lima
# Theme : Movement of a Plant around a fixed star
# Language : Python
# date : 11/19/2022
# Environment : Google Colab
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
from scipy.optimize import root
from scipy.linalg import eig
from mpl_toolkits.mplot3d import Axes3D
##################################
# Condições inicial e parãmetros #
##################################
t_inicial = 0
t_final = 100
N = 10000
h = 1e-3
x_0 = 1.0
y_0 = 1.0
z_0 = 1.0
#####################
# Equação de Lorenz #
#####################
def Lorenz(r,t,sigma,rho,beta):
x = r[0]; y = r[1]; z = r[2]
edo1 = sigma*(y-x)
edo2 = x*(rho-z)-y
edo3 = x*y-beta*z
return np.array([edo1,edo2,edo3])
t = np.linspace(t_inicial,t_final,N)
r_0 = np.array([x_0,y_0,z_0])
#sol = odeint(Lorenz,r_0,t,rtol=1e-6,args = (10,28,8/3))
sol = odeint(Lorenz, r_0, t, args=(10,28,8/3), Dfun=None, col_deriv=0, full_output=0, ml=None, mu=None, rtol=1e-9, atol=1e-9, tcrit=None, h0=0.0, hmax=0.0, hmin=0.0, ixpr=0, mxstep=0, mxhnil=0, mxordn=12, mxords=5, printmessg=0, tfirst=False)
'''x = sol[:,0]
y = sol[:,1]
z = sol[:,2]'''
x, y, z = sol.T
# Plot
plt.style.use('dark_background')
ax = plt.figure(figsize = (10,10)).add_subplot(projection='3d')
ax.plot(x,y,z,'m-',lw=0.5, linewidth = 1.5)
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Atrator de Lorenz")
plt.show()
In this second part, I simulate two Lorenz systems to verify the sensitive dependencies of the systems to the initial conditions. In the second system, I add a certain amount of eps = 1e-3 to the initial conditions of x(t0), y(t0) and z(t0).
# Depedência com as condições iniciais
eps = 1e-3
r_0_eps = np.array([x_0+eps,y_0+eps,z_0+eps])
sol_eps = odeint(Lorenz, r_0_eps, t, args=(10,28,8/3), Dfun=None, col_deriv=0, full_output=0, ml=None, mu=None,
rtol=1e-9, atol=1e-9, tcrit=None, h0=0.0, hmax=0.0, hmin=0.0, ixpr=0, mxstep=0, mxhnil=0, mxordn=12, mxords=5, printmessg=0, tfirst=False)
'''x_eps = sol_eps[:,0]
y_eps = sol_eps[:,1]
z_eps = sol_eps[:,2]'''
x_eps, y_eps, z_eps = sol_eps.T
# Plot
plt.style.use('dark_background')
ax = plt.figure(figsize = (10,10)).add_subplot(projection='3d')
ax.plot(x,y,z,'r-',lw=1.5)
ax.plot(x_eps,y_eps,z_eps,'b-.',lw=1.1)
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Lorenz Attractor")
plt.show()
Hope I helped, see you :).