Trouble Solving and Plotting Solution to Simple Differential Equation - python

I'm trying to solve the differential equation R^{2} = 1/R with initial condition that R(0) = 0 in python. I should get the solution that R'(t) = (3/2 * t)^(2/3) as I get this from mathematica. Plot of solution to R'[t]^2 = 1/R with initial condition R(0) = 0
I used the following code in python:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
sqrt = np.sqrt
# function that returns dy/dt
def model(y,t):
#k = 1
dydt = sqrt(1/y)
return dydt
# initial condition
y0 = [0.0]
# time points
t = np.linspace(0,5)
# solve ODE
y = odeint(model,y0,t)
# plot results
plt.plot(t,y)
plt.ylabel('$R/R_0$')
plt.xticks([])
plt.yticks([])
plt.show()
however I get only 0 as I'm apparently dividing by zero at some point python plot of differential equation R'[t]^2 = 1/R, which is not correct. Could someone point out what I could do to get the solution and plot I am expecting.
Thank you

Your model needs to be changed.I get your equation for the solution would be something like this https://www.wolframalpha.com/input/?i=R%27%28t%29%5E2+%3D+1%2FR
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
# function that returns dy/dt
def model(y,t):
#k = 1
dydt = 1*y**-1/2
return dydt
# initial condition
y0 = 0.1
# time points
t = np.linspace(0,20)
# solve ODE
y = odeint(model,y0,t)
# plot results
plt.plot(t,y)
plt.ylabel('$R$')
plt.xlabel('$t$')
plt.xticks([])
plt.yticks([])
plt.show()

Your starting value is 0, which results in the derivative being zero (y * -1, or simpler -y), which means 0 will get added to your current y-value, thus remaining at zero for the whole integration. Your code is correct, but not your formulation.
I see a 1/R in your link, so use that, e.g. dydt = 1/y; which will fail, because it results in division by zero, so don't start at zero, because your derivative is not defined there. There also appears to be a square root somewhere, that you imported, but never use.

Related

Can "fsolve (scipy)" find many roots of a function?

I read in the scipy documentation (https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fsolve.html) that fsolve : Find the rootS of a function.
But even, with a simple example, I obtained only one root
def my_eq(x):
y = x*x-1
return y
roots = fsolve(equation,0.1)
print(roots)
So, is it possible to obtain multiple roots with fsolve in one call ?
For other solvers, it is clear in the doc (https://docs.scipy.org/doc/scipy/reference/optimize.html#id2). They Find a root of a function
(N.B. I know that multiple root finding is difficult)
Apparently, the docs are a bit vague in that respect. The plural roots refers to the fact that both scipy.optimize.root and scipy.optimize.fsolve try to find one N-dimensional point x (root) of a multivariate function F: R^N -> R^N with F(x) = 0.
However, if you want to find multiple roots of your scalar function, you can write it as a multivariate function and pass different initial guesses:
import numpy as np
from scipy.optimize import fsolve
def my_eq(x):
y1 = x[0]*x[0]-1
y2 = x[1]*x[1]-1
return np.array([y1, y2])
x0 = [0.1, -0.1]
roots = fsolve(my_eq,[0.1, -0.1])
print(roots)
This yields all roots [1, -1].
scipy.optimize.root or scipy.optimize.fsolve only returns a single root closest to the x0 unless the func is a system of equations. So I think it cannot return multiple roots at once given only one scalar equation in this case.
A work around:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import fsolve
def func(x):
f = x ** 2 - 1
return f
# find the roots between 5 and -5
v1 = fsolve(func, np.array(5)) # solution closest to x= 5 is 1
v2 = fsolve(func, np.array(-5)) # solution closest to x= -5 is -1
fig, ax = plt.subplots()
t = np.linspace(-5, 5, 200)
ax.plot(t, func(t), label='$y = x^2 -1$')
ax.plot(t, np.zeros(t.shape), label='$y = 0$')
ax.plot(v1, func(v1[0]), "o", label=f'root1 = {v1[0]}')
ax.plot(v2, func(v2[0]), "o", label=f'root2 = {v2[0]}')
ax.legend()
print(f'root1 = {v1[0]}, root2 = {v2[0]}')
plt.show()

Checking the result of solve_ivp with solve_bvp - solve_bvp problems

I am hoping to use scipy.integrate.solve_bvp to solve a 2nd order differential equation: I am checking my process with a previous equation, so I am confident in moving onto more complex equations.
We begin with the differential equation system:
f''(x) + f(x) - f(x)^3 = 0
subject to the boundary conditions
f(x=0) = 0 f(x->infty) = gammaA
where gammaA is some constant between 0 and 1. I am finding numerical solutions for this, and comparing to a known analytic form (at least, for gammaA =1, a tanh function). For any given gammaA, we can integrate this equation once to and utilise the BC at infinity.
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import solve_ivp
gammaA = 0.9
xstart = 0.0
xend = 10
steps = 0.1
x = np.arange(xstart,xend,steps)
def dpsidx3(x,psi, gammaA):
eq = ( gammaA**2 *(1 - (1/2)*gammaA**2) - psi**2 *(1 - (1/2)*psi**2) )**0.5
return eq
psi0 = 0
x0 = xstart
x1 = xend
sol = solve_ivp(dpsidx3, [x0, x1], y0 = [psi0], args = (gammaA,), dense_output=True, rtol = 1e-9)
plotsol = sol.sol(x)
plt.plot(x, plotsol.T,marker = "", linestyle="--",label = r"Numerical solution - $solve\_ivp$")
plt.xlabel('x')
plt.ylabel('psi')
plt.legend()
plt.show()
If gammaA is not 1, then there are some runtime warnings but the shape is exactly as expected.
However, the ODE in the solve_ivp code has been manipulated into a form which is a 1st order ODE; for further work (with more complex and variable coefficients in the ODE), this will not be possible. Hence, I am trying to solve the boundary value problem using solve_bvp.
I am trying to solve now the same ODE, but I am not getting the same result as from this solution; the documentation is unclear on how to effectively use solve_bvp to me! Here is my attempt so far:
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import solve_bvp
gammaA = 0.9
xstart = 0.0
xend = 10
steps = 0.1
def fun(x,u):
du1 = u[1] #u[1] = u2, u[0] = u1
du2 = u[0]**3 - u[0]
return np.vstack( (du1, du2) )
def bc(ua, ub):
return np.array( [ua[0], ub[0]-gammaA])
x = np.linspace(xstart, xend, 10)
print(x.size)
y_a = np.zeros((2, x.size))
y_a[0] = np.linspace(0, gammaA, 10)
y_a[0] = gammaA
res_a = solve_bvp(fun, bc, x, y_a, max_nodes=100000, tol=1e-9)
print(res_a)
x_plot = np.linspace(0, xend, 100)
y_plot_a = res_a.sol(x_plot)[0]
fig2,ax2= plt.subplots()
ax2.plot(x_plot, y_plot_a, label=r'BVP solve')
ax2.legend()
ax2.set_xlabel("x")
ax2.set_ylabel("psi")
I have tried to write the 2nd order ODE as a system of 1st order ODEs and set the correct boundary conditions at the end of the system (rather than at infinity). I expected a similar tanh-function (where I could say that after the end of the system, my solution is simply gammaA, as expected by the asymptote), but it is clear that I am not getting this for any value of gammaA. Any advice gratefully appreciated; how can I reproduce the result of solve_ivp in solve_bvp?
EDIT: extra thoughts.
Can I add an additional constraint to my problem to ensure that the solution has a stationary point at the edge/is a monotonically increasing solution? The plots look okay for gammaA =1, but do not show the correct behaviour for any other values as in solve_ivp.
EDIT2: comparative figures, showing poor agreement with gammaA, e.g. 0.8 but good agreement for gammaA = 1.
You are making unfounded assumption on the mathematical nature of this equation. There is an energy functional
E = u'^2 + u^2 - 0.5*u^4 - 0.5 = u'^2 - 0.5*(u^2-1)^2
The solution that you first computed is at energy level 0.
For any smaller, negative energy level, roughly inside the unit circle, you get periodic oscillating solutions, these have no limit at infinity.
For larger, positive energy levels the solutions are unbounded, will rapidly grow larger, possibly diverge in finite time. Also here the limit at infinity either does not exist, as there is no solution connecting the initial point to large times, or the limit is infinity itself.
Forcing boundary conditions against this nature might work, but will not give a stable solution.

Solving an ordinary differential equation on a fixed grid (preferably in python)

I have a differential equation of the form
dy(x)/dx = f(y,x)
that I would like to solve for y.
I have an array xs containing all of the values of x for which I need ys.
For only those values of x, I can evaluate f(y,x) for any y.
How can I solve for ys, preferably in python?
MWE
import numpy as np
# these are the only x values that are legal
xs = np.array([0.15, 0.383, 0.99, 1.0001])
# some made up function --- I don't actually have an analytic form like this
def f(y, x):
if not np.any(np.isclose(x, xs)):
return np.nan
return np.sin(y + x**2)
# now I want to know which array of ys satisfies dy(x)/dx = f(y,x)
Assuming you can use something simple like Forward Euler...
Numerical solutions will rely on approximate solutions at previous times. So if you want a solution at t = 1 it is likely you will need the approximate solution at t<1.
My advice is to figure out what step size will allow you to hit the times you need, and then find the approximate solution on an interval containing those times.
import numpy as np
#from your example, smallest step size required to hit all would be 0.0001.
a = 0 #start point
b = 1.5 #possible end point
h = 0.0001
N = float(b-a)/h
y = np.zeros(n)
t = np.linspace(a,b,n)
y[0] = 0.1 #initial condition here
for i in range(1,n):
y[i] = y[i-1] + h*f(t[i-1],y[i-1])
Alternatively, you could use an adaptive step method (which I am not prepared to explain right now) to take larger steps between the times you need.
Or, you could find an approximate solution over an interval using a coarser mesh and interpolate the solution.
Any of these should work.
I think you should first solve ODE on a regular grid, and then interpolate solution on your fixed grid. The approximate code for your problem
import numpy as np
from scipy.integrate import odeint
from scipy import interpolate
xs = np.array([0.15, 0.383, 0.99, 1.0001])
# dy/dx = f(x,y)
def dy_dx(y, x):
return np.sin(y + x ** 2)
y0 = 0.0 # init condition
x = np.linspace(0, 10, 200)# here you can control an accuracy
sol = odeint(dy_dx, y0, x)
f = interpolate.interp1d(x, np.ravel(sol))
ys = f(xs)
But dy_dx(y, x) should always return something reasonable (not np.none).
Here is the drawing for this case

Python solving 2nd order ODE with quad function

I am studying the dynamics of a damped, driven pendulum with second order ODE defined like so, and specifically I am progamming:
d^2y/dt^2 + c * dy/dt + sin(y) = a * cos(wt)
import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate
def pendeq(t,y,a,c,w):
y0 = -c*y[1] - np.sin(y[0]) + a*np.cos(w*t)
y1 = y[1]
z = np.array([y0, y1])
return z
a = 2.1
c = 1e-4
w = 0.666667 # driving angular frequency
t = np.array([0, 5000]) # interval of integration
y = np.array([0, 0]) # initial conditions
yp = integrate.quad(pendeq, t[0], t[1], args=(y,a,c,w))
This problem does look quite similar to Need help solving a second order non-linear ODE in python, but I am getting the error
Supplied function does not return a valid float.
What am I doing wrong??
integrate.quad requires that the function supplied (pendeq, in your case) returns only a float. Your function is returning an array.

Fitting Differential Equations: curve_fit converges to local minima

I am trying to fit the differential equation ay' + by''=0 to a curve by varying a and b The following code does not work. The problem with curve_fit seems to be that lack of initial guess results in failure in fitting. I also tried leastsq. Can anyone suggest me other ways to fit such differential equation? If I don't have good guess curve_fit fails!
from scipy.integrate import odeint
from scipy.optimize import curve_fit
from numpy import linspace, random, array
time = linspace(0.0,10.0,100)
def deriv(time,a,b):
dy=lambda y,t : array([ y[1], a*y[0]+b*y[1] ])
yinit = array([0.0005,0.2]) # initial values
Y=odeint(dy,yinit,time)
return Y[:,0]
z = deriv(time, 2, 0.1)
zn = z + 0.1*random.normal(size=len(time))
popt, pcov = curve_fit(deriv, time, zn)
print popt # it only outputs the initial values of a, b!
Let's rewrite the equation:
ay' + by''=0
y'' = -a/b*y'
So this equation may be represented in this way
dy/dt = y'
d(y')/dt = -a/b*y'
The code in Python 2.7:
from scipy.integrate import odeint
from pylab import *
a = -2
b = -0.1
def deriv(Y,t):
'''Get the derivatives of Y at the time moment t
Y = [y, y' ]'''
return array([ Y[1], -a/b*Y[1] ])
time = linspace(0.0,1.0,1000)
yinit = array([0.0005,0.2]) # initial values
y = odeint(deriv,yinit,time)
figure()
plot(time,y[:,0])
xlabel('t')
ylabel('y')
show()
You may compare the resultant plots with the plots in WolframAlpha
If your problem is that the default initial guesses, read the documentation curve_fit to find out how to specify them manually by giving it the p0 parameter. For instance, curve_fit(deriv, time, zn, p0=(12, 0.23)) if you want a=12 and b=0.23 be the initial guess.

Categories

Resources