I am currently working on a set of code that I am using to solve an ordinary differential equation... My code is working, however, I would like to be able to modify it to solve the differential equation for a set of different constant values. This is what I have currently, and if ran works.
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def f(w, x):
# d1 = omega lambda
d1 = w
b2 = 0.0
# 0.2<c<1.4, 0.20 increments
c = 0.2
q = (1 - d1 - (2*((d1**1.5)/c))) / (2 + (3*(b2)))
f = (d1**2) * (1 - d1) * ((1 / d1) + (2 / (c * (d1**0.5))) - ((3 * b2 * q) / (d1 * (1-d1))))
return f
#determine domain, x
x = np.linspace(-80, 80, 1000001)
d1 = 10 ** -8
sol = odeint(f, d1, x)
plt.xlabel("x")
plt.ylabel("Omega Lambda")
plt.plot(x, sol, 'r')
plt.show()
However, I would like to construct a graph that consists of each line that a set of different values of c would produce... the c's I would like to produce a graph of are:
c = 0.2,0.4,0.6,0.8,1.0,1.2,1.4
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def f(w, x , b2 , c):
# d1 = omega lambda
d1 = w
# 0.2<c<1.4, 0.20 increments
#c = 0.2
q = (1 - d1 - (2*((d1**1.5)/c))) / (2 + (3*(b2)))
f = (d1**2) * (1 - d1) * ((1 / d1) + (2 / (c * (d1**0.5))) - ((3*b2*q)/(d1*1-d1))))
return f
#determine domain, x
x = np.linspace(-80, 80, 1000001)
d1 = 10 ** -8
b2 = 0.0
c = [ 0.2,0.4,0.6,0.8,1.0,1.2,1.4 ]
for i in c:
sol = odeint(f, d1, x, args = (b2 , i))
plt.xlabel("x")
plt.ylabel("Omega Lambda")
plt.plot(x, sol, 'r')
plt.show()
Related
I attempted to use the code below as a guide, in order to solve a non-linear equation, but I continue to get errors such as "object too deep for desired array" and "Result from function call is not a proper array of floats".
from scipy.optimize import fsolve
from math import exp
def equations(vars):
x, y = vars
eq1 = x+y**2-4
eq2 = exp(x) + x*y - 3
return [eq1, eq2]
x, y = fsolve(equations, (1, 1))
print(x, y)
My code will be posted below. It points out the error on the line "Q = fsolve(equations, 1)"
%reset -f
from math import *
T = 4 # N·m
ω = 1800*(pi/30) # rad/s
A1 = .00131 # m^2
A2 = .00055 # m^2
P1 = 12000 # Pa
P2 = 200000 # Pa
ρ = 1000 # kg/m^3
μ = .89e-3 # N·s/m^2
η = .57 # % efficiency
g = 9.81 # m/s^2
γ = 9810 # N/m^3
Z2 = .7 # m
P_motor = T*ω
print(P_motor)
P_pump = P_motor*η
print(P_pump)
from scipy.optimize import fsolve
def equations(vars):
Q = vars
eq1 = γ*Q*( ((P2-P1)/γ) + ( ( ( (Q**2) / (A2**2) ) - ( (Q**2) / (A1**2) ) )/2*g) + Z2) - P_pump
return [eq1]
Q = fsolve(equations, 1)
print(Q)
Since you have only one equation with one unknown variable, you don't need to put the output in a list. You can replace return [eq1] with return eq1.
The new code would be:
%reset -f
from math import *
T = 4 # N·m
ω = 1800*(pi/30) # rad/s
A1 = .00131 # m^2
A2 = .00055 # m^2
P1 = 12000 # Pa
P2 = 200000 # Pa
ρ = 1000 # kg/m^3
μ = .89e-3 # N·s/m^2
η = .57 # % efficiency
g = 9.81 # m/s^2
γ = 9810 # N/m^3
Z2 = .7 # m
P_motor = T*ω
print(P_motor)
P_pump = P_motor*η
print(P_pump)
from scipy.optimize import fsolve
def equations(vars):
Q = vars
eq1 = γ*Q*( ((P2-P1)/γ) + ( ( ( (Q**2) / (A2**2) ) - ( (Q**2) / (A1**2) ) )/2*g) + Z2) - P_pump
return eq1
Q = fsolve(equations, 1)
print(Q)
Output:
753.9822368615503
429.7698750110836
[0.0011589]
The documentation states
func : callable f(x, *args)
A function that takes at least one (possibly vector) argument, and returns a value of the same length.
if your input is a list of 2 values, it is expecting the function to return something of the same shape. So in your 1st example, you pass [x,y] and you return [eq1, eq2], so it works, but in second case, you pass a scalar and return a list
So, you can change your input to Q = fsolve(equations, (1,)) or change your returned value to return eq1:
def equations(vars):
Q = vars
eq1 = γ*Q*( ((P2-P1)/γ) + ( ( ( (Q**2) / (A2**2) ) - ( (Q**2) / (A1**2) ) )/2*g) + Z2) - P_pump
return eq1
I want to simulate the laser power along a silica fiber. The ODEs and the used parameters can be found in the paper linked below. Note that I converted all units to SI units in my code. The authors of this paper named the shooting method as a way to numerically solve this equations.
The paper I am referring to can be found here: https://www.osapublishing.org/oe/fulltext.cfm?uri=oe-21-17-20090&id=260516
I tried it in python but I did not manage to implement the root-finding. I used scypi.integrate_ivp to integrate the ODEs with a first guess. If I use a small value (~500m) at fiberlength I get a result but at a higher value in the range of km the solver does not manage to finish without an error. Can somebody please tell me how I can implement the shooting method here and also how I get the same solution as in the paper?
My code:
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import odeint
from scipy.integrate import solve_ivp
from scipy.integrate import solve_bvp
from scipy.optimize import fsolve
#%% ODEs und Parameter
#constants
alpha = 10**(-0.25/10)/1000 #fiber loss [1/m]
a0= alpha
a1= alpha
a2= alpha
a3= alpha
g = 0.53e-3 #RamanGain [1/(W*m)]
g1= g
g2= g*0.8
g3= g*0.6
epsilon0 = 1e-4*1e-3 #Rayleigh-scattering [1/m] REFERENZ: Paper: Third order random lasing via Raman gain and Rayleigh backscattering
epsilon1 = 5e-5*1e-3
epsilon2 = 2e-5*1e-3
epsilon3 = 1e-5*1e-3
wl_start = 1365e-9
RamanShift= 13.2e12
f0=3e8/wl_start
f1= f0 - RamanShift
f2= f1 - RamanShift
f3= f2 - RamanShift
#R_L1 = 0.99 # Reflexionskoeffizienten (Fasereingang FBG)
#R_L2 = 5e-3 # Reflexionskoeffizienten (Fasereingang)
#R_R1 = 4e-5 # Reflexionskoeffizienten (Faserausgang)
#R_R2 = 4e-5 # Reflexionskoeffizienten (Faserausgang)
R=0.6 # reflectivity
# G[i] = 4*h*f[i]*df[i]*(1+1/(np.exp((h*(f[i-1]-f[i]))/(K*T))-1))
h=6.63e-34 #Placksches Wirkungsquantum [Js]
K=1.38e-23 #Bolzmannkonstante [J/K]
T=300 #Temperature [K]
#df1= 0.18e12 #Bandbreite [Hz] REFERENZ: Paper: Third order random lasing via Raman gain and Rayleigh backscattering
#df2= 0.25e12
#df3= 0.25e12
#bandwidth
df1= 0.18e12
df2= 0.25e12
df3= 0.25e12
Gamma1= 4*h*f1*df1*(1+(1/(np.exp(h*(f0-f1)/(K*T))-1)))
Gamma2= 4*h*f2*df2*(1+(1/(np.exp(h*(f1-f2)/(K*T))-1)))
Gamma3= 4*h*f3*df3*(1+(1/(np.exp(h*(f2-f3)/(K*T))-1)))
#start conditions
Pin = 2.7 #W
fiberlength = 90000 #m
points = fiberlength*100
P0 = [Pin, 0, 0, 0, 0, 0, 0, 0 ]
def odes2 (z, P):
Pump_forward = P[0]
Pump_backward = P[1]
Stokes1_forward = P[2]
Stokes1_backward = P[3]
Stokes2_forward = P[4]
Stokes2_backward = P[5]
Stokes3_forward = P[6]
Stokes3_backward = P[7]
dPump_forwarddz = - a0 * Pump_forward - g1 * f0/f1 * Pump_forward * (Stokes1_forward + Stokes1_backward + Gamma1) + epsilon0 * Pump_backward
dPump_backwarddz = + a0 * Pump_backward + g1 * f0/f1 * Pump_backward * (Stokes1_forward + Stokes1_backward + Gamma1) - epsilon0 * Pump_forward
dStokes1_forwarddz = - a1 * Stokes1_forward - g2 * f1/f2 * Stokes1_forward * (Stokes2_forward + Stokes2_backward + Gamma2) + g1 * (Stokes1_forward + 0.5 * Gamma1) * (Pump_forward + Pump_backward) + epsilon1 * Stokes1_backward
dStokes1_backwardz = + a1 * Stokes1_backward + g2 * f1/f2 * Stokes1_backward * (Stokes2_forward + Stokes2_backward + Gamma2) - g1 * (Stokes1_backward + 0.5 * Gamma1) * (Pump_forward + Pump_backward) - epsilon1 * Stokes1_forward
dStokes2_forwarddz = - a2 * Stokes2_forward - g3 * f2/f3 * Stokes2_forward * (Stokes3_forward + Stokes3_backward + Gamma3) + g2 * (Stokes2_forward + 0.5 * Gamma2) * (Stokes1_forward + Stokes1_backward) + epsilon2 * Stokes2_backward
dStokes2_backwardz = + a2 * Stokes2_backward + g3 * f2/f3 * Stokes2_backward * (Stokes3_forward + Stokes3_backward + Gamma3) - g2 * (Stokes2_backward + 0.5 * Gamma2) * (Stokes1_forward + Stokes1_backward) - epsilon2 * Stokes2_forward
dStokes3_forwarddz = - a3 * Stokes3_forward + g3 * (Stokes3_forward + 0.5 * Gamma3) * (Stokes2_forward + Stokes2_backward) + epsilon3 * Stokes3_backward
dStokes3_backwardz = + a3 * Stokes3_backward - g3 * (Stokes3_backward + 0.5 * Gamma3) * (Stokes2_forward + Stokes2_backward) - epsilon3 * Stokes3_forward
return [dPump_forwarddz, dPump_backwarddz, dStokes1_forwarddz, dStokes1_backwardz, dStokes2_forwarddz, dStokes2_backwardz, dStokes3_forwarddz, dStokes3_backwardz]
sol = solve_ivp(odes2, (0, fiberlength), P0, t_eval=np.linspace(0, fiberlength, points))
Pump_f, Pump_b, Stokes1_f, Stokes1_b, Stokes2_f, Stokes2_b, Stokes3_f, Stokes3_b = sol.y
x=sol.t
plt.figure(1)
plt.title('backwards')
plt.plot(x, Pump_b, label='pump')
plt.plot(x, Stokes1_b, label='stokes#1')
plt.plot(x, Stokes2_b, label='stokes#2')
plt.plot(x, Stokes3_b, label='stokes#3')
plt.legend(loc=1, fontsize='xx-small')
plt.grid(True)
plt.figure(2)
plt.title('forwards')
plt.plot(x, Pump_f, label='pump')
plt.plot(x, Stokes1_f, label='stokes#1')
plt.plot(x, Stokes2_f, label='stokes#2')
plt.plot(x, Stokes3_f, label='stokes#3')
plt.legend(loc=1, fontsize='xx-small')
plt.grid(True)
I also tried the solve_bvp but it wasn't successfull.
In our physics class we have to model a damping torsional pendulum.
Ilustration of torsional pendulum:
We came up with this equation of motion:
Where θ is the angle, A is torsion parameter, B is Newton's parameter, C is Stokes' parameter, and D is friction parameter. We also use the sign function sgn that determines the direction of the acting force upon the pendulum, depending on the current angle from the reference point.
The problem is, that I'm unable to solve it using Runge-Kutta method in Python.
I got a working solution in MATLAB by using Euler's method, which has some flaws, but it is something.
MATLAB Code:
function [theta, dtheta, epsilon] = drt(t, theta0, dtheta0, A, B, C, D)
epsilon = zeros(1, length(t));
theta = epsilon;
dtheta = epsilon;
theta(1) = theta0;
dtheta(1) = dtheta0;
epsilon(1) = A * alpha0 - B * dtheta^2 - C * dtheta - D;
dt = t(2) - t(1);
for i = 1 : (length(t) - 1)
epsilon(i + 1)= - A * theta(i) - B * dtheta(i)^2 * sign(dtheta(i)) - C * dtheta(i) - D * sign(dtheta(i));
dtheta(i + 1)= dtheta(i) + dt * epsilon(i);
theta(i + 1) = theta(i) + dt * dtheta(i);
end
end
We call this MATLAB function like this for example:
t = linspace(0, 10, 100);
theta0 = 90;
dtheta0 = 0;
A = 1;
B = 0.1;
C = 0.1;
D = 0.1;
[theta, dtheta, epsilon] = drt(t, theta0, dtheta0, A, B, C, D);
We can then plot the theta and other values in a graph, which shows us, how the torsional pendulum is being damped by the external forces acting on it.
Python Code:
import numpy as np
import matplotlib.pyplot as plt
# Damping torsional pendulum
def drp(drp_alpha, drp_d_alpha, drp_params):
a = drp_params["tors"]
b = drp_params["newt"]
c = drp_params["stok"]
d = drp_params["fric"]
result = a * drp_alpha - b * np.power(drp_d_alpha, 2) * np.sign(drp_d_alpha) - c * drp_d_alpha - d * np.sign(drp_d_alpha)
return result
# Runge-Kutta 4th Order
# f - function DamRotPen
# x0 - initial condition
# t0 - initial time
# tmax - maximum time
# dt - sample time
def RG4(rg4_f, rg4_x0, rg4_t0, rg4_tmax, rg4_dt):
# Time vector
rg4_t = np.arange(rg4_t0, rg4_tmax, rg4_dt)
# Time vector size
rg4_t_sz = rg4_t.size
# Initialize the array
rg4_alpha = np.zeros(rg4_t_sz)
# Initial value of the system
rg4_alpha[0] = rg4_x0
for k in range(rg4_t_sz - 1):
k1 = dt * f(rg4_t[k], rg4_alpha[k])
k2 = dt * rg4_f(rg4_t[k] + dt / 2, rg4_alpha[k] + k1 / 2)
k3 = dt * rg4_f(rg4_t[k] + dt / 2, rg4_alpha[k] + k2 / 2)
k4 = dt * rg4_f(rg4_t[k] + dt, rg4_alpha[k] + k3)
rg4_d_alpha = (k1 + 2 * k2 + 2 * k3 + k4) / 6
rg4_alpha[k + 1] = rg4_alpha[k] + rg4_d_alpha
return rg4_alpha, rg4_t
# Parameters of the forces acting on the system
# tors - torsion parameter
# newt - Newton's parameter
# stok - Stokes' parameter
# fric - friction parameter
params = {"tors": 1, "newt": 0.1, "stok": 0.1, "fric": 0.1}
# Start parameters
alpha = 90
d_alpha = 0
# Initial time
t0 = 0
# Maximum time
tmax = 120
# Sample time
dt = 0.01
# Define DamRotPen function as 'f' using lambda
f = lambda t, alpha : drp(alpha, d_alpha, params)
# Try to solve this shit
alpha, t = RG4(f, alpha, t0, tmax, dt)
# Plot this shit
plt.plot(t, alpha, "r", "r", label="Position I guess")
plt.xlabel("Time t / s")
plt.grid()
plt.show()
After plotting the values, we can see on the graph, that the θ sky rockets after certain amount of time. I don't know what I'm doing wrong, I tried practically everything, so that's why I'm asking you for help. (Though I think part of the problem might be my misunderstanding on how to implement the Runge-Kutta method, maybe I got the math wrong, etc...)
This python code can solve one non- coupled differential equation:
import numpy as np
import matplotlib.pyplot as plt
import numba
import time
start_time = time.clock()
#numba.jit()
# A sample differential equation "dy / dx = (x - y**2)/2"
def dydx(x, y):
return ((x - y**2)/2)
# Finds value of y for a given x using step size h
# and initial value y0 at x0.
def rungeKutta(x0, y0, x, h):
# Count number of iterations using step size or
# step height h
n = (int)((x - x0)/h)
# Iterate for number of iterations
y = y0
for i in range(1, n + 1):
"Apply Runge Kutta Formulas to find next value of y"
k1 = h * dydx(x0, y)
k2 = h * dydx(x0 + 0.5 * h, y + 0.5 * k1)
k3 = h * dydx(x0 + 0.5 * h, y + 0.5 * k2)
k4 = h * dydx(x0 + h, y + k3)
# Update next value of y
y = y + (1.0 / 6.0)*(k1 + 2 * k2 + 2 * k3 + k4)
# Update next value of x
x0 = x0 + h
return y
def dplot(start,end,steps):
Y=list()
for x in np.linspace(start,end,steps):
Y.append(rungeKutta(x0, y, x , h))
plt.plot(np.linspace(start,end,steps),Y)
print("Execution time:",time.clock() - start_time, "seconds")
plt.show()
start,end = 0, 10
steps = end* 100
x0 = 0
y = 1
h = 0.002
dplot(start,end,steps)
This code can solve this differential equation:
dydx= (x - y**2)/2
Now I have a system of coupled differential equations:
dydt= (x - y**2)/2
dxdt= x*3 + 3y
How can I implement these two as a system of coupled differential equations in the above code?
Is there any more generalized way for system of n-number of coupled differential equations?
With the help of others, I got to this:
import numpy as np
from math import sqrt
import matplotlib.pyplot as plt
import numba
import time
start_time = time.clock()
a=1
b=1
c=1
d=1
# Equations:
#numba.jit()
#du/dt=V(u,t)
def V(u,t):
x, y, vx, vy = u
return np.array([vy,vx,a*x+b*y,c*x+d*y])
def rk4(f, u0, t0, tf , n):
t = np.linspace(t0, tf, n+1)
u = np.array((n+1)*[u0])
h = t[1]-t[0]
for i in range(n):
k1 = h * f(u[i], t[i])
k2 = h * f(u[i] + 0.5 * k1, t[i] + 0.5*h)
k3 = h * f(u[i] + 0.5 * k2, t[i] + 0.5*h)
k4 = h * f(u[i] + k3, t[i] + h)
u[i+1] = u[i] + (k1 + 2*(k2 + k3 ) + k4) / 6
return u, t
u, t = rk4(V, np.array([1., 0., 1. , 0.]) , 0. , 10. , 100000)
x,y, vx,vy = u.T
# plt.plot(t, x, t,y)
plt.semilogy(t, x, t,y)
plt.grid('on')
print("Execution time:",time.clock() - start_time, "seconds")
plt.show()
I am trying to solve a simple ODE so as to understand the new API of Scipy.
I wrote a routine for Runge Kutta of order 4 to write it and confirmed it with the old API odeint and it matched beautifully. But now that I am trying to get around the solve_ivp, it seems that is not working. What am I getting wrong?
import numpy as np
from matplotlib import pyplot as plt
from scipy.integrate import solve_ivp, odeint
import time
freq = np.arange(1, 10000, 100)
def g(q, t):
return -q ** 3 + np.sin(t)
a = 0
b = 10
npoints = 100
h = (b - a) / npoints
t = np.arange(a, b, h)
output1 = np.zeros(t.shape)
x = 0
for i in range(len(t)):
output1[i] = x
k1 = h * g(x, t[i])
k2 = h * g(x + 0.5 * k1, t[i] + 0.5 * h)
k3 = h * g(x + 0.5 * k2, t[i] + 0.5 * h)
k4 = h * g(x + k3, t[i] + 0.5 * h)
x = x + 1 / 6 * (k1 + 2 * k2 + 2 * k3 + k4)
# ---------------Solving using odeint (old API)---------------#
y1_odeint = odeint(g, 0, t)
#---------------Solving using new API-------------#
y2=solve_ivp(g,(a,b),[0],t_eval=t)
# --------------------Representação gráfica--------------------------#
fig = plt.figure()
ax = fig.add_subplot(121)
ax1=fig.add_subplot(122)
ax.plot(t, output1,label="my own")
ax.plot(t,y1_odeint,label="odeint")
ax.plot(y2.t,np.squeeze(y2.y),label="new API")
ax.legend()
ax.set_title("Output")
ax1.plot(t,output1-np.squeeze(y1_odeint),label="|odeint-my own|")
ax1.legend()
plt.tight_layout()
plt.show()
Take another look at the docstring for solve_ivp. It expects the first argument of g to be t. By default, odeint uses the opposite convention. If you have a recent version of scipy, you can tell odeint that the first argument is t by giving it the argument tfirst=True.