1

So, I wrote a simple code to create an animated graph using Matplotlib's FuncAnimation. But there is no output. I think the fault is in the 'np.append' function as the code works if I give the 'animate' function pre-made x, y, z arrays. But, I can't understand why THIS does not work!

%matplotlib notebook

import numpy as np
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.pyplot as plt
import matplotlib.animation as animation

del_t = 0.01 ##Time-step value

x=np.array([0.0])
y=np.array([10.0])
z=np.array([0.0])

#These functions give how (x, y, z) co-ordinate
#changes with time
def dx_dt(x, y, z):
    return 10*(y-x)
def dy_dt(x, y, z):
    return -x*z + 28*x - y
def dz_dt(x, y, z):
    return x*y-(8/3)*z

#Runge-Kutta Method for numerical solution of differential equations
#These functions give next (x, y, z) co-ordinate
def next_xpt(x, y, z):
    k1 = dx_dt(x, y, z) * del_t
    k2 = dx_dt(x + k1/2, y, z) * del_t
    k3 = dx_dt(x + k2/2, y, z) * del_t
    k4 = dx_dt(x + k3, y, z) * del_t
    return x + (k1 + 2*k2 + 2*k3 + k4)/6
def next_ypt(x, y, z):
    k1 = dy_dt(x, y, z) * del_t
    k2 = dy_dt(x, y + k1/2, z) * del_t
    k3 = dy_dt(x, y + k2/2, z) * del_t
    k4 = dy_dt(x, y + k3, z) * del_t
    return y + (k1 + 2*k2 + 2*k3 + k4)/6
def next_zpt(x, y, z):
    k1 = dz_dt(x, y, z) * del_t
    k2 = dz_dt(x, y, z + k1/2) * del_t
    k3 = dz_dt(x, y, z + k2/2) * del_t
    k4 = dz_dt(x, y, z + k3) * del_t
    return z + (k1 + 2*k2 + 2*k3 + k4)/6

fig = plt.figure()
ax = p3.Axes3D(fig)

#Creating a line object
line, = ax.plot3D([0.0],[10.0],[0.0],'-b') 

ax.set_xlim3d(-30,30)
ax.set_xlabel("X")
ax.set_ylim3d(-30,30)
ax.set_ylabel("Y")
ax.set_zlim3d(-30,30)
ax.set_zlabel("Z")
ax.set_title("Lorenz Strange Attractor")

def animate(i, x, y, z, line):
    np.append(x, next_xpt(x[i], y[i], z[i]))
    np.append(y, next_ypt(x[i], y[i], z[i]))
    np.append(z, next_zpt(x[i], y[i], z[i]))
    line.set_data(x[:i+1],y[:i+1])
    line.set_3d_properties(z[:i+1])
    return line

ani = animation.FuncAnimation(fig, animate, fargs = (x, y, z, line), interval=50, blit=False)
sussy_baka
  • 13
  • 2
  • reread the docs for np.append. it does not work like the list append – hpaulj Mar 17 '19 at 11:00
  • Thanks for the reply. But, I've read them. And the format I've used for np.append is correct, I guess, because I checked that separately. Here, x or y or z are ndarrays and 'next_xpt' or 'next_ypt' or 'next_zpt' are functions that return float. – sussy_baka Mar 17 '19 at 11:48
  • The name `np.append` fools users into thinking it operates in-place like the list append method. It's just a cover function for `np.concatenate`. In your case collecting the values in lists might be faster and simpler. – hpaulj Mar 17 '19 at 16:09
  • Note that the numerical method is not RK4 but some convoluted way to implement an order 1 method, on par with the explicit Euler method. You need to solve a coupled system as a coupled system. – Lutz Lehmann Mar 18 '19 at 10:21
  • @LutzL Thank you for your advice! Please, could you help me out with that and tell me a little more detail? – sussy_baka Mar 19 '19 at 12:36
  • Going the totally opposite way, even needlessly avoiding lists for the 3D vectors, as the challenge was to do it without any numerical package: https://stackoverflow.com/q/53908604/3088138. Also does not have the animation. – Lutz Lehmann Mar 19 '19 at 13:23
  • Another implementation with a small (or large) error is https://stackoverflow.com/q/24060778/3088138 using arrays properly for vector operations. One could make it a little shorter by using more python constructs, but not by much. – Lutz Lehmann Mar 19 '19 at 13:33

1 Answers1

0

You need to save the result of append in the arrays x, y and z. The reason you saw it appending was because you were testing it interactively most probably. But as @hpaulj mentioned, for your purpose, you will have to store the appended array in the function. Moreover, you will have to declare x, y, z as global to reflect the changes to avoid the IndexError. To initialize the line object, you can define an init function and then just pass the iterable index i in your FuncAnimation

The docs say (emphasis mine)

Returns: append : ndarray

A copy of arr with values appended to axis. Note that append does not occur in-place: a new array is allocated and filled.

You will have to store the new array

# Initizlise x, y, z here

def init():
    line.set_data([], [])
    line.set_3d_properties([])
    return line,

# dx_dt, dy_dt, dz_dt etc. functions here 

fig = plt.figure()
ax = p3.Axes3D(fig)

#Creating a line object
line, = ax.plot3D([0.0],[10.0],[0.0],'-b') 

# Setting axes limits here    

def animate(i):
    global x, y, z
    x = np.append(x, next_xpt(x[i], y[i], z[i]))
    y = np.append(y, next_ypt(x[i], y[i], z[i]))
    z = np.append(z, next_zpt(x[i], y[i], z[i]))
    line.set_data(x[:i+1],y[:i+1])
    line.set_3d_properties(z[:i+1])
    return line,

ani = animation.FuncAnimation(fig, animate, init_func=init, interval=50, blit=False)
Community
  • 1
  • 1
Sheldore
  • 37,862
  • 7
  • 57
  • 71
  • Thanks for the answer. You pointed that out correctly. I implemented that, but still, I don't understand why but the arrays are not getting updated. The error still shows that index 1 is out of bounds for array of size 1. – sussy_baka Mar 17 '19 at 12:31
  • @AdityaDutta: I see, that is probably because your arrays x, y, z are defined outside the function so every time you exit the function, your x, y, z loses the memory of previous x, y, z. Can you try adding `global x, y, z` as the first line inside your `def animate` and see if you still get this error? – Sheldore Mar 17 '19 at 12:35
  • It shows an error : name 'x' is parameter and global – sussy_baka Mar 17 '19 at 12:49
  • Yes, that works! You are awesome! I got the point. It was exactly as you said in the previous comment. I shouldn't have used x,y,z as arguments which was the source of that last error. Even if I use xs,ys,zs as arguements and then declare x,y,z as global variables inside animate, it works just as fine! – sussy_baka Mar 17 '19 at 13:11
  • I tried your way too. But the current answer is far simple with less variables – Sheldore Mar 17 '19 at 13:15
  • Yes, absolutely! Thankyou – sussy_baka Mar 17 '19 at 13:18