Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Cleanup differential equations examples. #22043

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions examples/animation/double_pendulum.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from numpy import sin, cos
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
import matplotlib.animation as animation
from collections import deque

Expand All @@ -22,13 +21,13 @@
L = L1 + L2 # maximal length of the combined pendulum
M1 = 1.0 # mass of pendulum 1 in kg
M2 = 1.0 # mass of pendulum 2 in kg
t_stop = 5 # how many seconds to simulate
t_stop = 2.5 # how many seconds to simulate
history_len = 500 # how many trajectory points to display


def derivs(state, t):

def derivs(t, state):
dydx = np.zeros_like(state)

dydx[0] = state[1]

delta = state[2] - state[0]
Expand All @@ -51,7 +50,7 @@ def derivs(state, t):
return dydx

# create a time array from 0..t_stop sampled at 0.02 second steps
dt = 0.02
dt = 0.01
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check if this is really necessary. This doubles the number of frames and thus likely the size of the generated file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decreased the end time by a factor of two as well; the shorter time step does appear to improve the accuracy.

Copy link
Member

@timhoffm timhoffm Dec 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that means that you're only going half the path from before. Please check if that's still a reasonable path length. I'm afraid that might be a bit short.

I can imagine that the shorter time step improves accuracy. But does that matter here? Alternatively, one could also render only every second step, but that adds additional compelity to the code and I'm not sure that's worth it.

Of course, one could also change to another example like a regular single pendulum. That's simpler to calculate and one could have a continuous cyclic animation with, a period of e.g. 2s.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did check, the path length is fine.

t = np.arange(0, t_stop, dt)

# th1 and th2 are the initial angles (degrees)
Expand All @@ -64,8 +63,15 @@ def derivs(state, t):
# initial state
state = np.radians([th1, w1, th2, w2])

# integrate your ODE using scipy.integrate.
y = integrate.odeint(derivs, state, t)
# integrate the ODE using Euler's method
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes the example longer and IMHO a little harder to understand. Does this outweigh the benefit of not depending on scipy in this example?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only other use of scipy in the docs can also easily be removed (I have another patch ready for that) so we could entirely drop the dependency on scipy for building the docs, which may or may not be a useful thing (e.g., with the yearly release cycle of cpython, I fear that we're always going to have a small period between having scipy wheels available after the new cpython release, and scipy is significantly harder to build from source (e.g., due to fortran sources) than other dependencies, even numpy).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think dropping SciPy would be pretty helpful if we don't need it. I think its a pretty heavy dependency to carry around for a couple of examples.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have oscillated back and forth on having a scipy documentation dependencies a couple of times. I do not have a strong preference either way.

y = np.empty((len(t), 4))
y[0] = state
for i in range(1, len(t)):
y[i] = y[i - 1] + derivs(t[i - 1], y[i - 1]) * dt

# A more accurate estimate could be obtained e.g. using scipy:
#
# y = scipy.integrate.solve_ivp(derivs, t[[0, -1]], state, t_eval=t).y.T

x1 = L1*sin(y[:, 0])
y1 = -L1*cos(y[:, 0])
Expand Down
40 changes: 18 additions & 22 deletions examples/mplot3d/lorenz_attractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,41 @@
import matplotlib.pyplot as plt


def lorenz(x, y, z, s=10, r=28, b=2.667):
def lorenz(xyz, *, s=10, r=28, b=2.667):
"""
Given:
x, y, z: a point of interest in three dimensional space
s, r, b: parameters defining the lorenz attractor
Returns:
x_dot, y_dot, z_dot: values of the lorenz attractor's partial
derivatives at the point x, y, z
Parameters
----------
xyz : array-like, shape (3,)
Point of interest in three dimensional space.
s, r, b : float
Parameters defining the Lorenz attractor.

Returns
-------
xyz_dot : array, shape (3,)
Values of the Lorenz attractor's partial derivatives at *xyz*.
"""
x, y, z = xyz
x_dot = s*(y - x)
y_dot = r*x - y - x*z
z_dot = x*y - b*z
return x_dot, y_dot, z_dot
return np.array([x_dot, y_dot, z_dot])


dt = 0.01
num_steps = 10000

# Need one more for the initial values
xs = np.empty(num_steps + 1)
ys = np.empty(num_steps + 1)
zs = np.empty(num_steps + 1)

# Set initial values
xs[0], ys[0], zs[0] = (0., 1., 1.05)

xyzs = np.empty((num_steps + 1, 3)) # Need one more for the initial values
xyzs[0] = (0., 1., 1.05) # Set initial values
# Step through "time", calculating the partial derivatives at the current point
# and using them to estimate the next point
for i in range(num_steps):
x_dot, y_dot, z_dot = lorenz(xs[i], ys[i], zs[i])
xs[i + 1] = xs[i] + (x_dot * dt)
ys[i + 1] = ys[i] + (y_dot * dt)
zs[i + 1] = zs[i] + (z_dot * dt)

xyzs[i + 1] = xyzs[i] + lorenz(xyzs[i]) * dt

# Plot
ax = plt.figure().add_subplot(projection='3d')

ax.plot(xs, ys, zs, lw=0.5)
ax.plot(*xyzs.T, lw=0.5)
ax.set_xlabel("X Axis")
ax.set_ylabel("Y Axis")
ax.set_zlabel("Z Axis")
Expand Down