import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
#%matplotlib inline
#np.seterr(divide='ignore');
# Dynamics function
def simple_golf(t, z):
'''
z[0] = x(t)
z[1] = y(t)
z[3] = y'(t)
'''
# Vx is defined globally
return [Vx, z[2] , -9.81]
# IVP parameters
tspan = [0, 10]
y0 = [0, 0, 15]
Vx = 25.
# Call the numerical solver
sol = solve_ivp(simple_golf, tspan, y0, max_step=1) # try adding max_step=1
type(sol)
scipy.integrate._ivp.ivp.OdeResult
sol
message: The solver successfully reached the end of the integration interval. success: True status: 0 t: [ 0.000e+00 3.430e-05 ... 9.381e+00 1.000e+01] y: [[ 0.000e+00 8.574e-04 ... 2.345e+02 2.500e+02] [ 0.000e+00 5.145e-04 ... -2.909e+02 -3.405e+02] [ 1.500e+01 1.500e+01 ... -7.703e+01 -8.310e+01]] sol: None t_events: None y_events: None nfev: 92 njev: 0 nlu: 0
print(sol.t)
print(len(sol.t))
[0.00000000e+00 3.42974305e-05 3.77271736e-04 3.80701479e-03 3.81044453e-02 3.81078751e-01 1.38107875e+00 2.38107875e+00 3.38107875e+00 4.38107875e+00 5.38107875e+00 6.38107875e+00 7.38107875e+00 8.38107875e+00 9.38107875e+00 1.00000000e+01] 16
print(len(sol.y))
print(sol.y)
3 [[ 0.00000000e+00 8.57435763e-04 9.43179339e-03 9.51753697e-02 9.52611133e-01 9.52696876e+00 3.45269688e+01 5.95269688e+01 8.45269688e+01 1.09526969e+02 1.34526969e+02 1.59526969e+02 1.84526969e+02 2.09526969e+02 2.34526969e+02 2.50000000e+02] [ 0.00000000e+00 5.14455688e-04 5.65837789e-03 5.70341319e-02 5.64444871e-01 5.00387218e+00 1.13604896e+01 7.90710710e+00 -5.35627544e+00 -2.84296580e+01 -6.13130405e+01 -1.04006423e+02 -1.56509806e+02 -2.18823188e+02 -2.90946571e+02 -3.40500000e+02] [ 1.50000000e+01 1.49996635e+01 1.49962990e+01 1.49626532e+01 1.46261954e+01 1.12616175e+01 1.45161746e+00 -8.35838254e+00 -1.81683825e+01 -2.79783825e+01 -3.77883825e+01 -4.75983825e+01 -5.74083825e+01 -6.72183825e+01 -7.70283825e+01 -8.31000000e+01]]
# Plot the solution
plt.plot(sol.y[0], sol.y[1], 'ro-');
plt.title('Golf Ball Trajectory')
plt.xlabel('Distance (m)')
plt.ylabel('Height (m)');
# The system state at the end
sol.y[:,-1]
array([ 250. , -340.5, -83.1])
# Suppose we want to pass Vx as a parameter to our dynamics function, like this.
def simple_golf2(t, z, Vx):
'''
z[0] = x(t)
z[1] = y(t)
z[2] = y'(t)
'''
return [Vx, z[2] , -9.81]
# We can create a simple wrapper for our dynamics function, like this:
fun = lambda t,x: simple_golf2(t,x,20) # Vx will be passed through as a constant
sol = solve_ivp(fun, tspan, y0, max_step=1.)
# Or use the args argument, like this:
sol = solve_ivp(simple_golf2, tspan, y0, max_step=1., args=(20,))
# Plot
plt.plot(sol.y[0], sol.y[1], 'ro-');
plt.title('Golf Ball Trajectory')
plt.xlabel('Distance (m)')
plt.ylabel('Height (m)');
# Maximum step size (max_step)
sol = solve_ivp(simple_golf, tspan, y0, max_step=0.5)
plt.plot(sol.y[0], sol.y[1], 'ro-')
plt.xlabel('x'); plt.ylabel('y');
sol.t
array([0.00000000e+00, 3.42974305e-05, 3.77271736e-04, 3.80701479e-03, 3.81044453e-02, 3.81078751e-01, 8.81078751e-01, 1.38107875e+00, 1.88107875e+00, 2.38107875e+00, 2.88107875e+00, 3.38107875e+00, 3.88107875e+00, 4.38107875e+00, 4.88107875e+00, 5.38107875e+00, 5.88107875e+00, 6.38107875e+00, 6.88107875e+00, 7.38107875e+00, 7.88107875e+00, 8.38107875e+00, 8.88107875e+00, 9.38107875e+00, 9.88107875e+00, 1.00000000e+01])
np.diff(sol.t)
array([3.42974305e-05, 3.42974305e-04, 3.42974305e-03, 3.42974305e-02, 3.42974305e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 5.00000000e-01, 1.18921249e-01])
# Choosing solution timestamps (t_eval)
tt = np.arange(tspan[0], tspan[1], 0.2)
sol = solve_ivp(simple_golf, tspan, y0, t_eval=tt)
plt.plot(sol.y[0], sol.y[1], 'ro-')
plt.xlabel('x'); plt.ylabel('y');
# Choosing error tolerances (rtol, atol)
fun = (lambda t,z: 2*z[0]*(1-z[0])/(1+t))
sol1 = solve_ivp(fun, [0,10], [0.3], rtol=1.e-2, atol=1.e-12)
sol2 = solve_ivp(fun, [0,10], [0.3], rtol=1.e-7, atol=1.e-7)
plt.plot(sol1.t, sol1.y[0], 'ro-')
plt.plot(sol2.t, sol2.y[0], 'bo-')
plt.xlabel('x'); plt.ylabel('y');
# Define the event function
def my_event(t,z,blah):
return z[1]
# We want integration to terminate
my_event.terminal = True
# And we want only positive-to-negative crossings.
my_event.direction = -1
#fun = lambda t,x: simple_golf2(t,x,10)
sol = solve_ivp(simple_golf2, tspan, y0, max_step=0.6, args=(20,), events=my_event)
plt.plot(sol.y[0], sol.y[1], 'ro-', ms=3);
plt.title('Golf Ball Trajectory')
plt.xlabel('Distance (m)')
plt.ylabel('Height (m)');
sol.y[1]
array([0.00000000e+00, 5.99952156e-04, 6.59861055e-03, 6.64988780e-02, 6.56869903e-01, 5.69741525e+00, 1.03158158e+01, 1.14026163e+01, 8.95781689e+00, 2.98141744e+00, 1.33226763e-15])