Scipy minimize with contraints and bounds in output - python

I'm working with a General Equlibrium model. I'm trying to minimize an objective function to get the productivity in each sector of economy. I have two main functions, equilibrium, and obj. The input of the equilibrium function is x1, that is the productivity of each i sectores. The main output of this function is the share of workers (L_m). The share of workers for all i is positive and the sum is the unity.
So I need find x1 that minimize my objective function objand satisfies the restrictions in L_m.
I make the following:
import numpy as np
from scipy.optimize import minimize
# Define parameters of function
def pars():
i = 10
theta = 3
w = np.ones(i).reshape(i, 1)
L = np.ones(i).reshape(i, 1)
beta = np.random.uniform(low = 1e-4, high = 0.99, size = (100, 1)).reshape(10, 10)
sigma = np.random.uniform(low = 1e-3, high = 0.91, size = (i, 1))
VA_d = np.random.uniform(low = 1e-3, high = 1, size = (i, 1))
x1 = np.random.uniform(low = 1e-3, high = 30, size = (i, 1))
return [theta, w, i, L, beta, sigma, VA_d, x1]
[theta, w, i, L, beta, sigma, VA_d, x1] = pars()
def equlibrium(x1, beta, sigma):
sig1 = 1 - sigma
phi = np.multiply(np.power(sig1, sigma), np.power(sigma, sigma) )
# Bp matrix
id = np.eye(i)
fid = id == 1
Bp = np.multiply(beta.T, sig1).T
Bp[fid] = Bp[fid] - 1
Bp = np.multiply(-1, Bp)
# D object
W = np.log(np.divide(w, np.multiply(phi, x1) ) )
K = np.multiply(sig1, np.sum( np.multiply(beta, np.log(beta) ), axis = 1).reshape(i, 1) )
D = W - K
# prices
log_p = np.dot(np.linalg.inv(Bp), D)
p = np.exp(log_p)
#### Objects B and G
# Object B
B = np.multiply(np.divide(sig1, sigma), np.divide(beta, p.T) )
# Object G
g1 = np.power(np.divide(sig1, sigma), sig1)
g2 = np.prod( np.power(np.divide(beta, p.T), np.multiply(beta, sig1) ), axis = 1 ).reshape(i, 1)
G = np.multiply(x1, np.multiply(g1, g2))
#### consumption
c1 = np.divide(1, np.power(p, theta))
c2 = np.divide(np.multiply(w, L), np.sum(np.power(p, (1 - theta) ) ) )
C = np.multiply(c1, c2)
#### B e C divided by G
B_til = np.divide(B, G.T) # retirei o transposto do G
C_til = np.divide(C, G)
##### labor
L_m = np.dot(np.linalg.inv(np.eye(i) - B_til.T), C_til)
#L_m = L_m/np.sum(L_m) # a soma de L_i = L = 1
##### calcular o X e o Q
X = np.multiply(B, L_m)
Q = np.multiply(G, L_m)
####### ADDED VALUE
VA_m = np.divide(np.multiply(p, C), L_m)
###### Eq solve
#EQ_solve = np.multiply(np.multiply(p, sig1), np.multiply(beta, np.divide(G, B)) ) - p
return [p, C, B, G, L_m, VA_m]
My objective function is:
def obj1(x1):
x1 = x1.reshape(i, 1)
[p, C, B, G, L_m, VA_m] = equlibrium(x1 = x1, beta = beta, sigma = sigma)
#Q - C - X.sum(1).reshape(i, 1) = np.zeros(i).reshape(i, 1)
obj = np.sum( np.power(np.divide((VA_m - VA_d), VA_m), 2) )
obj = np.log(obj)
return obj
def callback(x1):
global cc
cc += 1
fobj = obj1(x1)
print(f'\033[1;033mObjetivo: {np.around(fobj, 8)}, iter: {cc}')
First I use L-BFGS-B algorithm, and in equilibrium function I put L_m = L_m/np.sum(L_m). This works well in optimization, but, the equilibrium np.multiply(G, L_m) - np.multiply(B, L_m) - C has to be zero. And proceeding in this way, it does not happen.
cc = 0
Bd = ( (1e-1, 1000), )*i
sol = minimize(obj1, x1, method='L-BFGS-B', bounds = Bd, tol = 1e-20, callback = callback, options={'maxiter':300, 'maxls':1e5, 'maxfun':1e10, 'maxcor': 4000, 'eps': 1e-10})
So I made a constraint and switched the algorithm to SLSQP.
def restr(x1):
[p, C, B, G, L_m, VA_m] = equlibrium(x1, beta, sigma)
return np.sum(L_m) - 1
cons = ({'type': 'eq', 'fun': lambda x1: restr(x1) } )
sol = minimize(obj1, x1, method='trust-constr', constraints = cons, bounds=Bd, options = {'maxiter':5000} )
However, that definitely didn't work. It seems that the algorithm was successful in optimizing, but it did not respect the constraints. How to proceed to solve this problem?

Related

Runge Kutta 4th order Python

I am trying to solve this equation using Runge Kutta 4th order:
applying d2Q/dt2=F(y,x,v) and dQ/dt=u Q=y in my program.
I try to run the code but i get this error:
Traceback (most recent call last):
File "C:\Users\Egw\Desktop\Analysh\Askhsh1\asdasda.py", line 28, in <module>
k1 = F(y, u, x) #(x, v, t)
File "C:\Users\Egw\Desktop\Analysh\Askhsh1\asdasda.py", line 13, in F
return ((Vo/L -(R0/L)*u -(R1/L)*u**3 - y*(1/L*C)))
OverflowError: (34, 'Result too large')
I tried using the decimal library but I still couldnt make it work properly.I might have not used it properly tho.
My code is this one:
import numpy as np
from math import pi
from numpy import arange
from matplotlib.pyplot import plot, show
#parameters
R0 = 200
R1 = 250
L = 15
h = 0.002
Vo=1000
C=4.2*10**(-6)
t=0.93
def F(y, u, x):
return ((Vo/L -(R0/L)*u -(R1/L)*u**3 - y*(1/L*C)))
xpoints = arange(0,t,h)
ypoints = []
upoints = []
y = 0.0
u = Vo/L
for x in xpoints:
ypoints.append(y)
upoints.append(u)
m1 = u
k1 = F(y, u, x) #(x, v, t)
m2 = h*(u + 0.5*k1)
k2 = (h*F(y+0.5*m1, u+0.5*k1, x+0.5*h))
m3 = h*(u + 0.5*k2)
k3 = h*F(y+0.5*m2, u+0.5*k2, x+0.5*h)
m4 = h*(u + k3)
k4 = h*F(y+m3, u+k3, x+h)
y += (m1 + 2*m2 + 2*m3 + m4)/6
u += (k1 + 2*k2 + 2*k3 + k4)/6
plot(xpoints, upoints)
show()
plot(xpoints, ypoints)
show()
I expected to get the plots of u and y against t.
Turns out I messed up with the equations I was using for Runge Kutta
The correct code is the following:
import numpy as np
from math import pi
from numpy import arange
from matplotlib.pyplot import plot, show
#parameters
R0 = 200
R1 = 250
L = 15
h = 0.002
Vo=1000
C=4.2*10**(-6)
t0=0
#dz/dz
def G(x,y,z):
return Vo/L -(R0/L)*z -(R1/L)*z**3 - y/(L*C)
#dy/dx
def F(x,y,z):
return z
t = np.arange(t0, 0.93, h)
x = np.zeros(len(t))
y = np.zeros(len(t))
z = np.zeros(len(t))
y[0] = 0.0
z[0] = 0
for i in range(1, len(t)):
k0=h*F(x[i-1],y[i-1],z[i-1])
l0=h*G(x[i-1],y[i-1],z[i-1])
k1=h*F(x[i-1]+h*0.5,y[i-1]+k0*0.5,z[i-1]+l0*0.5)
l1=h*G(x[i-1]+h*0.5,y[i-1]+k0*0.5,z[i-1]+l0*0.5)
k2=h*F(x[i-1]+h*0.5,y[i-1]+k1*0.5,z[i-1]+l1*0.5)
l2=h*G(x[i-1]+h*0.5,y[i-1]+k1*0.5,z[i-1]+l1*0.5)
k3=h*F(x[i-1]+h,y[i-1]+k2,z[i-1]+l2)
l3 = h * G(x[i - 1] + h, y[i - 1] + k2, z[i - 1] + l2)
y[i]=y[i-1]+(k0+2*k1+2*k2+k3)/6
z[i] = z[i - 1] + (l0 + 2 * l1 + 2 * l2 + l3) / 6
Q=y
I=z
plot(t, Q)
show()
plot(t, I)
show()
If I may draw your attention to these 4 lines
m1 = u
k1 = F(y, u, x) #(x, v, t)
m2 = h*(u + 0.5*k1)
k2 = (h*F(y+0.5*m1, u+0.5*k1, x+0.5*h))
You should note a fundamental structural difference between the first two lines and the second pair of lines.
You need to multiply with the step size h also in the first pair.
The next problem is the step size and the cubic term. It contributes a term of size 3*(R1/L)*u^2 ~ 50*u^2 to the Lipschitz constant. In the original IVP per the question with u=Vo/L ~ 70 this term is of size 2.5e+5. To compensate only that term to stay in the stability region of the method, the step size has to be smaller 1e-5.
In the corrected initial conditions with u=0 at the start the velocity u remains below 0.001 so the cubic term does not determine stability, this is now governed by the last term contributing a Lipschitz term of 1/sqrt(L*C) ~ 125. The step size for stability is now 0.02, with 0.002 one can expect quantitatively useful results.
You can use decimal libary for more precision (handle more digits), but it's kind of annoying every value should be the same class (decimal.Decimal).
For example:
import numpy as np
from math import pi
from numpy import arange
from matplotlib.pyplot import plot, show
# Import decimal.Decimal as D
import decimal
from decimal import Decimal as D
# Precision
decimal.getcontext().prec = 10_000_000
#parameters
# Every value should be D class (decimal.Decimal class)
R0 = D(200)
R1 = D(250)
L = D(15)
h = D(0.002)
Vo = D(1000)
C = D(4.2*10**(-6))
t = D(0.93)
def F(y, u, x):
# Decomposed for use D
a = D(Vo/L)
b = D(-(R0/L)*u)
c = D(-(R1/L)*u**D(3))
d = D(-y*(D(1)/L*C))
return ((a + b + c + d ))
xpoints = arange(0,t,h)
ypoints = []
upoints = []
y = D(0.0)
u = D(Vo/L)
for x in xpoints:
ypoints.append(y)
upoints.append(u)
m1 = u
k1 = F(y, u, x) #(x, v, t)
m2 = (h*(u + D(0.5)*k1))
k2 = (h*F(y+D(0.5)*m1, u+D(0.5)*k1, x+D(0.5)*h))
m3 = h*(u + D(0.5)*k2)
k3 = h*F(y+D(0.5)*m2, u+D(0.5)*k2, x+D(0.5)*h)
m4 = h*(u + k3)
k4 = h*F(y+m3, u+k3, x+h)
y += (m1 + D(2)*m2 + D(2)*m3 + m4)/D(6)
u += (k1 + D(2)*k2 + D(2)*k3 + k4)/D(6)
plot(xpoints, upoints)
show()
plot(xpoints, ypoints)
show()
But even with ten million of precision I still get an overflow error. Check the components of the formula, their values are way too high. You can increase precision for handle them, but you'll notice it takes time to calculate them.
Problem implementation using scipy.integrate.odeint and scipy.integrate.solve_ivp.
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint, solve_ivp
# Input data initial conditions
ti = 0.0
tf = 0.5
N = 100000
h = (tf-ti)/N
# Initial conditions
u0 = 0.0
Q0 = 0.0
t_span = np.linspace(ti,tf,N)
r0 = np.array([Q0,u0])
# Parameters
R0 = 200
R1 = 250
L = 15
C = 4.2*10**(-6)
V0 = 1000
# Systems of First Order Equations
# This function is used with odeint, as specified in the documentation for scipy.integrate.odeint
def f(r,t,R0,R1,L,C,V0):
Q,u = r
ode1 = u
ode2 = -((R0/L)*u)-((R1/L)*u**3)-((1/(L*C))*Q)+(V0/L)
return np.array([ode1,ode2])
# This function is used in our 4Order Runge-Kutta implementation and in scipy.integrate.solve_ivp
def F(t,r,R0,R1,L,C,V0):
Q,u = r
ode1 = u
ode2 = -((R0/L)*u)-((R1/L)*u**3)-((1/(L*C))*Q)+(V0/L)
return np.array([ode1,ode2])
# Resolution with oedint
sol_1 = odeint(f,r0,t_span,args=(R0,R1,L,C,V0))
sol_2 = solve_ivp(fun=F,t_span=(ti,tf), y0=r0, method='LSODA',args=(R0,R1,L,C,V0))
Q_odeint, u_odeint = sol_1[:,0], sol_1[:,1]
Q_solve_ivp, u_solve_ivp = sol_2.y[0,:], sol_2.y[1,:]
# Figures
plt.figure(figsize=[30.0,10.0])
plt.subplot(3,1,1)
plt.grid(color = 'red',linestyle='--',linewidth=0.4)
plt.plot(t_span,Q_odeint,'r',t_span,u_odeint,'b')
plt.xlabel('t(s)')
plt.ylabel('Q(t), u(t)')
plt.subplot(3,1,2)
plt.plot(sol_2.t,Q_solve_ivp,'g',sol_2.t,u_solve_ivp,'y')
plt.grid(color = 'yellow',linestyle='--',linewidth=0.4)
plt.xlabel('t(s)')
plt.ylabel('Q(t), u(t)')
plt.subplot(3,1,3)
plt.plot(Q_solve_ivp,u_solve_ivp,'green')
plt.grid(color = 'yellow',linestyle='--',linewidth=0.4)
plt.xlabel('Q(t)')
plt.ylabel('u(t)')
plt.show()
Runge-Kutta 4th
# Code development of Runge-Kutta 4 Order
# Parameters
R0 = 200
R1 = 250
L = 15
C = 4.2*10**(-6)
V0 = 1000
# Input data initial conditions #
ti = 0.0
tf = 0.5
N = 100000
h = (tf-ti)/N
# Initial conditions
u0 = 0.0
Q0 = 0.0
# First order ordinary differential equations
def f1(t,Q,u):
return u
def f2(t,Q,u):
return -((R0/L)*u)-((R1/L)*u**3)-((1/(L*C))*Q)+(V0/L)
t = np.zeros(N); Q = np.zeros(N); u = np.zeros(N)
t[0] = ti
Q[0] = Q0
u[0] = u0
for i in range(0,N-1,1):
k1 = h*f1(t[i],Q[i],u[i])
l1 = h*f2(t[i],Q[i],u[i])
k2 = h*f1(t[i]+(h/2),Q[i]+(k1/2),u[i]+(l1/2))
l2 = h*f2(t[i]+(h/2),Q[i]+(k1/2),u[i]+(l1/2))
k3 = h*f1(t[i]+(h/2),Q[i]+(k2/2),u[i]+(l2/2))
l3 = h*f2(t[i]+(h/2),Q[i]+(k2/2),u[i]+(l2/2))
k4 = h*f1(t[i]+h,Q[i]+k3,u[i]+l3)
l4 = h*f2(t[i]+h,Q[i]+k3,u[i]+l3)
Q[i+1] = Q[i] + ((k1+2*k2+2*k3+k4)/6)
u[i+1] = u[i] + ((l1+2*l2+2*l3+l4)/6)
t[i+1] = t[i] + h
plt.figure(figsize=[20.0,10.0])
plt.subplot(1,2,1)
plt.plot(t,Q_solve_ivp,'r',t,Q_odeint,'y',t,Q,'b')
plt.grid(color = 'yellow',linestyle='--',linewidth=0.4)
plt.xlabel('t(s)')
plt.ylabel(r'$Q(t)_{Odeint}$, $Q(t)_{RK4}$')
plt.subplot(1,2,2)
plt.plot(t,Q_solve_ivp,'g',t,Q_odeint,'y',t,Q,'b')
plt.grid(color = 'yellow',linestyle='--',linewidth=0.4)
plt.xlabel('t(s)')
plt.ylabel(r'$Q(t)_{solve_ivp}$, $Q(t)_{RK4}$')

Odeint problem in model: lsoda-- repeated occurrences of illegal input && lsoda-- at start of problem, too much accuracy

I need help to solve a ODE system using odeint or any workaround that works. I`m trying to model a ODE system descriving the beheaviour of a cilinder on a mechanichal arm, using Lagrangian mechanics I got the following 2nd order ODE system:
$$\ddot{x} = x*\dot{\phi}^2 - g*(sin(\phi) +cos(\phi))$$
$$\ddot{\phi} = \frac{1}{2*\dot{x}*m_c}*(\frac{k*i}{x}-\frac{m_b*L*\dot{\phi}}{2}-g*cos(\phi)*(m_c*x-m_b*L/2))$$
Then I transformed it to a 4x4 1st order ODE system y linearized 2 equations aroun initial condition, to make it more "solveable".
In python I`wrote the folllowing:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
# source: https://apmonitor.com/pdc/index.php/Main/SolveDifferentialEquations
g, mc, mb, k, L = (9.8, 0.5, 1.3, 2, 1.2)
def model(z, t, i, z0):
global g, mc, mb, k, L
#ODE variables
x1 = z[0]
y1 = z[1]
x2 = z[2]
y2 = z[3]
#ODE initial conditions
zx1 = z0[0]
zy1 = z0[1]
zx2 = z0[2]
zy2 = z0[3]
#Diferential for linealization around I.C.
dx1 = z[0]-z0[0]
dy1 = z[1]-z0[1]
dx2 = z[2]-z0[2]
dy2 = z[3]-z0[3]
def w1(zx1, zy1, zx2, zy2):
global g, mc, mb, k, L
return zx2*zy1**2-g*(np.sin(zy2)+np.cos(zy2))
def w2(zx1, zy1, zx2, zy2):
global g, mc, mb, k, L
return 1/(2*zx1*mc)*((k*i)/(zx2)-mb*zy2*L/2-g*np.cos(zy2*(mc*zx2-mb*L/2)))
dx2dt = x1
#dx1dt = x2*y1**2-g*(np.sin(y2)+np.cos(y2))
dx1dt = w1(zx1, zy1, zx2, zy2) + zy1**2*dx2+2*zx2 * \
zy1*dy1-g*(np.cos(zy2)-np.sin(zy2))*dy2
dy2dt = y1
#dy1dt = 1/(2*x1*mc)*((k*i)/(x2)-mb*y2*L/2-g*np.cos(y2*(mc*x2-mb*L/2)))
dy1dt = w2(zx1, zy1, zx2, zy2) - 1/(4*zx1**2*mc) * \
((k*i)/(zx2)-mb*zy2*L/2-g*np.cos(zy2*(mc*zx2-mb*L/2)))*dx1
+(k*i*np.log(abs(zx2))/(2*zx1*mc)-g*np.cos(zy2)*mc)*dx2-(mb*L) / \
(4*zx1*mc)*dy1+g*np.sin(zy2)*(mc*zx2-mb*L/2)*(2*zx1*mc)*dy2
dzdt = [dx1dt, dy1dt, dx2dt, dy2dt]
return dzdt
# initial condition
z0 = [0.01, 0.01, 0.8, np.pi/8]
# number of time points
n = 30
# time points
t = np.linspace(0, 1, n)
# step input
u = np.zeros(n)
# change to 2.0 at time = 5.0
u[:] = 1
# store solution
x1 = np.zeros(n)
y1 = np.zeros(n)
x2 = np.zeros(n)
y2 = np.zeros(n)
# record initial conditions
x1[0] = z0[0]
y1[0] = z0[1]
x2[0] = z0[2]
y2[0] = z0[3]
# solve ODE
for i in range(1, n):
# span for next time step
tspan = [t[i-1], t[i]]
# solve for next step
z = odeint(model, z0, tspan, args=((u[i], z0)) )
# store solution for plotting
if -np.pi/4 <= z[1][3] <= np.pi/4: # Angulo +/- Pi/4
u[i] = u[i-1] + 1
else:
u[i] = u[i-1]
if 0 <= z[1][2] <= L: # Se mantiene en el brazo
print("Se saliΓ³")
x1[i] = z[1][0]
y1[i] = z[1][1]
x1[i] = z[1][2]
y1[i] = z[1][3]
# Siguientes CI
z0 = z[1]
# plot results
plt.plot(t, u, 'g:', label='u(t)')
plt.plot(t, x2, 'b-', label='x(t)')
plt.plot(t, y2, 'r--', label='phi(t)')
plt.ylabel('Cilindro(x,phi)')
plt.xlabel('Tiempo(s)')
plt.legend(loc='best')
plt.show()
Then I`ve got the following error, telling me than too much accuracy is needed to solve my system:
lsoda-- at start of problem, too much accuracy
requested for precision of machine.. see tolsf (=r1)
in above message, r1 = NaN
lsoda-- repeated occurrences of illegal input
lsoda-- at start of problem, too much accuracy
requested for precision of machine.. see tolsf (=r1)
in above message, r1 = NaN
dy1dt = w2(zx1, zy1, zx2, zy2) - 1/(4*zx1**2*mc) * \
lsoda-- warning..internal t (=r1) and h (=r2) are
such that in the machine, t + h = t on the next step
(h = step size). solver will continue anyway
in above, r1 = 0.3448275862069D+00 r2 = 0.1555030227910D-28

Solving set of ODEs with Scipy

I am trying to develop an algorithm (use scipy.integrate.odeint()) that predicts the changing concentration of cells, substrate and product (i.e., 𝑋, 𝑆, 𝑃) over time until the system reaches steady- state (~100 or 200 hours). The initial concentration of cells in the bioreactor is 0.1 𝑔/𝐿 and there is no glucose or product in the reactor initially. I want to test the algorithm for a range of different flow rates, 𝑄, between 0.01 𝐿/β„Ž and 0.25 𝐿/β„Ž and analyze the impact of the flow rate on product production (i.e., 𝑄 β‹… 𝑃 in 𝑔/β„Ž). Eventually, I would like to generate a plot that shows product production rate (y-axis) versus flow rate, 𝑄, on the x-axis. My goal is to estimate the flow rate that results in the maximum (or critical) production rate. This is my code so far:
from scipy.integrate import odeint
import numpy as np
# Constants
u_max = 0.65
K_s = 0.14
K_1 = 0.48
V = 2
X_in = 0
S_in = 4
Y_s = 0.38
Y_p = 0.2
# Variables
# Q - Flow Rate (L/h), value between 0.01 and 0.25 that produces best Q * P
# X - Cell Concentration (g/L)
# S - The glucose concentration (g/L)
# P - Product Concentration (g/L)
# Equations
def func_dX_dt(X, t, S):
u = (u_max) / (1 + (K_s / S))
dX_dt = (((Q * S_in) - (Q * S)) / V) + (u * X)
return dX_dt
def func_dS_dt(S, t, X):
u = (u_max) / (1 + (K_s / S))
dS_dt = (((Q * S_in) - (Q * S)) / V) - (u * (X / Y_s))
return dS_dt
def func_dP_dt(P, t, X, S):
u = (u_max) / (1 + (K_s / S))
dP_dt = ((-Q * P) / V) - (u * (X / Y_p))
return dP_dt
t = np.linspace(0, 200, 200)
# Q placeholder
Q = 0.01
# Attempt to solve the Ordinary differential equations
sol_dX_dt = odeint(func_dX_dt, 0.1, t, args=(S,))
sol_dS_dt = odeint(func_dS_dt, 0.1, t, args=(X,))
sol_dP_dt = odeint(func_dP_dt, 0.1, t, args=(X,S))
In the programs current state there does not seem to be be a way to generate the steady state value for P. I attempted to make this modification to get the value of X.
sol_dX_dt = odeint(func_dX_dt, 0.1, t, args=(odeint(func_dS_dt, 0.1, t, args=(X,)),))
It produces the error:
NameError: name 'X' is not defined
At this point I am not sure how to move forward.
(Edit 1: Added Original Equations)
First Equation
Second Equation and Third Equation
You do not have to apply the functions to each part but return a tuple of the derivatives as I show below:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
Q = 0.01
V = 2
Ys = 0.38
Sin = 4
Yp = 0.2
Xin = 0
umax = 0.65
Ks = 0.14
K1 = 0.48
def mu(S, umax, Ks, K1):
return umax/((1+Ks/S)*(1+S/K1))
def dxdt(x, t, *args):
X, S, P = x
Q, V, Xin, Ys, Sin, Yp, umax, Ks, K1 = args
m = mu(S, umax, Ks, K1)
dXdt = (Q*Xin - Q*X)/V + m*X
dSdt = (Q*Sin - Q*S)/V - m*X/Ys
dPdt = -Q*P/V - m*X/Yp
return dXdt, dSdt, dPdt
t = np.linspace(0, 200, 200)
X0 = 0.1
S0 = 0.1
P0 = 0.1
x0 = X0, S0, P0
sol = odeint(dxdt, x0, t, args=(Q, V, Xin, Ys, Sin, Yp, umax, Ks, K1))
plt.plot(t, sol[:, 0], 'r', label='X(t)')
plt.plot(t, sol[:, 1], 'g', label='S(t)')
plt.plot(t, sol[:, 2], 'b', label='P(t)')
plt.legend(loc='best')
plt.xlabel('t')
plt.grid()
plt.show()
Output:

RK4-method and Euler method working only for certain formulae

I tried to write an algorithm for solving the non-linear ODE
dr/dt = r(t)+rΒ²(t)
which has (one possible) solution
r(t) = rΒ²(t)/2+rΒ³(t)/3
For that I implemented both the euler method and the RK4 method in python. For error checking I used the example on rosettacode:
dT/dt = -k(T(t)-T0)
with the solution
T0 + (Ts βˆ’ T0)*exp(βˆ’kt)
Thus, my code now looks like
import numpy as np
from matplotlib import pyplot as plt
def test_func_1(t, x):
return x*x
def test_func_1_sol(t, x):
return x*x*x/3.0
def test_func_2_sol(TR, T0, k, t):
return TR + (T0-TR)*np.exp(-0.07*t)
def rk4(func, dh, x0, t0):
k1 = dh*func(t0, x0)
k2 = dh*func(t0+dh*0.5, x0+0.5*k1)
k3 = dh*func(t0+dh*0.5, x0+0.5*k2)
k4 = dh*func(t0+dh, x0+k3)
return x0+k1/6.0+k2/3.0+k3/3.0+k4/6.0
def euler(func, x0, t0, dh):
return x0 + dh*func(t0, x0)
def rho_test(t0, rho0):
return rho0 + rho0*rho0
def rho_sol(t0, rho0):
return rho0*rho0*rho0/3.0+rho0*rho0/2.0
def euler2(f,y0,a,b,h):
t,y = a,y0
while t <= b:
#print "%6.3f %6.5f" % (t,y)
t += h
y += h * f(t,y)
def newtoncooling(time, temp):
return -0.07 * (temp - 20)
x0 = 100
x_vec_rk = []
x_vec_euler = []
x_vec_rk.append(x0)
h = 1e-3
for i in range(100000):
x0 = rk4(newtoncooling, h, x0, i*h)
x_vec_rk.append(x0)
x0 = 100
x_vec_euler.append(x0)
x_vec_sol = []
x_vec_sol.append(x0)
for i in range(100000):
x0 = euler(newtoncooling, x0, 0, h)
#print(i, x0)
x_vec_euler.append(x0)
x_vec_sol.append(test_func_2_sol(20, 100, 0, i*h))
euler2(newtoncooling, 0, 0, 1, 1e-4)
x_vec = np.linspace(0, 1, len(x_vec_euler))
plt.plot(x_vec, x_vec_euler, x_vec, x_vec_sol, x_vec, x_vec_rk)
plt.show()
#rho-function
x0 = 1
x_vec_rk = []
x_vec_euler = []
x_vec_rk.append(x0)
h = 1e-3
num_steps = 650
for i in range(num_steps):
x0 = rk4(rho_test, h, x0, i*h)
print "%6.3f %6.5f" % (i*h, x0)
x_vec_rk.append(x0)
x0 = 1
x_vec_euler.append(x0)
x_vec_sol = []
x_vec_sol.append(x0)
for i in range(num_steps):
x0 = euler(rho_test, x0, 0, h)
print "%6.3f %6.5f" % (i*h, x0)
x_vec_euler.append(x0)
x_vec_sol.append(rho_sol(i*h, i*h+x0))
x_vec = np.linspace(0, num_steps*h, len(x_vec_euler))
plt.plot(x_vec, x_vec_euler, x_vec, x_vec_sol, x_vec, x_vec_rk)
plt.show()
It works fine for the example from rosettacode, but it is unstable and explodes (for t > 0.65, both for RK4 and euler) for my formula. Is therefore my implementation incorrect, or is there another error I do not see?
Searching for the exact solution of your equation:
dr/dt = r(t)+rΒ²(t)
I've found:
r(t) = exp(C+t)/(1-exp(C+t))
Where C is an arbitrary constant which depends on initial conditions. It can be seen that for t -> -C the r(t) -> infinity.
I do not know what initial condition you use, but may be you meet with this singularity when calculate numerical solution.
UPDATE:
Since your initial condition is r(0)=1 the constant C is C = ln(1/2) ~ -0.693. It can explain why your numerical solution crashes at some t>0.65
UPDATE:
To verify your code you can simply compare your numerical solution calculated for, say 0<=t<0.6 with exact solution.

Plotting projectile motion of 1 y-position values vs. 2 x-position values using matplotlib and numpy

Hi i'm trying to get a plot of the trajectory of a mass under projectile motion. One with a force acting on the horizontal axis and one without (basically 2 sets of x values plotted against a 1 set of y-values). Here's what i have so far.. I'm new to programming and i can't seem to figure out where this went wrong. Hope you guys can help me. Thank you!
import numpy as np
import matplotlib.pyplot as pl
def position(y0, v0, theta, g, t):
y= y0 + v0*np.sin(theta)*t + (g*t**2)/2
return y
def position2(x0, v0, theta, c, e, alpha, t):
x1 = x0 + v0*(np.cos(theta))*t + c*(t*(e-1)+(2-2*e)/alpha)
return x1
def position3(x0, v0, theta, t):
x2 = x0 + v0*(np.cos(theta))*t
return x2
t = np.linspace(0,10,1000)
#part1
m = 1
theta = 45
y0 = 2
x0 = 0
v0 = 3
k = 1
alpha = 0.5
g = -9.8
c = (-k/m)*(1/alpha**2)
e = -(np.e**(-alpha*t))
x1 = []
x2 = []
y = []
for a in t:
x1_data = position2(x0, v0, theta, c, e, alpha, t)
x1.append(x1_data)
x2_data = position3(x0, v0, theta, t)
x2.append(x2_data)
y_data = position(y0, v0, theta, g, t)
y.append(y_data)
print x1_data
print x2_data
print y_data
pl.title('Constant and Time-Dependent Forces')
pl.xlabel(b'x-position')
pl.ylabel(b'y-position')
x1label = 'projectile 1'
x2label = "'normal' projectile"
plot1 = pl.plot(x1_data, y, 'r')
plot2 = pl.plot(x2_data, y, 'b')
pl.legend()
pl.show()
I went through your code since i am new to matplotlib and wanted to play a bit with it. The only mistake i found is in the for loop where you do for a in t: but end up passing t to the functions instead of a.
import numpy as np
import matplotlib.pyplot as pl
sin = np.sin
cos = np.cos
pi = np.pi
def y_position(y0, v0, phi, g, t):
y_t = y0 + v0 * sin(phi) * t + (g * t**2) / 2
return y_t
def x_position_force(x0, v0, phi, k, m, alpha, t):
term1 = (-k / m) * (1 / alpha ** 2)
term2 = -np.e ** (-alpha * t)
x_t = x0 + v0 * cos(phi) * t + term1 * (t * (term2 - 1) + (2 - 2 * term2) / alpha)
return x_t
def x_position_no_force(x0, v0, phi, t):
x_t = x0 + v0 * cos(phi) * t
return x_t
time = np.linspace(0, 10, 100)
#------------- I N P U T -------------#
x_init = 0
y_init = 2
v_init = 3
theta = 45
gravity = -9.8
m = 1
k = 1
alpha = 0.5
#------------- I N P U T -------------#
x_with_force = []
x_with_no_force = []
y = []
for time_i in time:
x_with_force.append(x_position_force(x_init, v_init, theta, k, m, alpha, time_i))
x_with_no_force.append(x_position_no_force(x_init, v_init, theta, time_i))
y.append(y_position(y_init, v_init, theta, gravity, time_i))
# print(x1_data)
# print(x2_data)
# print(y_data)
pl.subplot(211)
pl.title('Constant and Time-Dependent Forces')
pl.xlabel('time')
plot1 = pl.plot(time, x_with_force, 'r', label='x_coord_dynamicF')
plot2 = pl.plot(time, x_with_no_force, 'g', label='x_coord_staticF')
plot3 = pl.plot(time, y, 'b', label='y_coord')
pl.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.)
pl.subplot(212)
pl.title('Trajectory (x,y)')
pl.xlabel('X')
pl.ylabel('Y')
plot4 = pl.plot(x_with_force, y, 'r^')
plot5 = pl.plot(x_with_no_force, y, 'b*')
pl.show()
I changed a number of things though to make the code inline with PEP8. In my opinion it is the use of bad variable names that lead you to the mistake you did. So i would recommend taking the time to type those few extra characters that ultimately help you and the people reading your code.

Categories