1

I am trying to create a (very) simple N-body simulation and animate it in 3D. Here is my code:

from mpl_toolkits.mplot3d import Axes3D

import numpy as np
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt

G=6.67408e-11
msol=1.989e40
mter=5.972e24
au=1.496e11
dt=.0007
N=30
positions=np.random.rand(N,3)
velocities=np.random.randn(N,3)
acc=np.zeros_like(positions)
masses=np.random.rand(N)

def gravforce(m0,m1,pos0,pos1):
    global G
    dx=pos1[0]-pos0[0]
    dy=pos1[1]-pos0[1]
    dz=pos1[2]-pos0[2]
    r=np.sqrt(dx**2+dy**2+dz**2)
    f=-G*m0*m1/r**2
    ratio=f/r
    fx=dx*ratio
    fy=dy*ratio
    fz=dz*ratio
    return fx, fy, fz

fig,ax=plt.subplots(subplot_kw=dict(projection='3d'))
planets=ax.scatter(positions[:,0],positions[:,1],positions[:,2],c='b',marker='o')

def animate(i):
    acc[:,0]=[sum([gravforce(masses[i],masses[j],positions[i],positions[j])[0]/masses[j] for j in range(1,N) if j != i]) for i in range(len(acc))]
    acc[:,1]=[sum([gravforce(masses[i],masses[j],positions[i],positions[j])[1]/masses[j] for j in range(1,N) if j != i]) for i in range(len(acc))]
    acc[:,2]=[sum([gravforce(masses[i],masses[j],positions[i],positions[j])[2]/masses[j] for j in range(1,N) if j != i]) for i in range(len(acc))]

    velocities[:,0]=velocities[:,0]+acc[:,0]*dt
    velocities[:,1]=velocities[:,1]+acc[:,1]*dt
    velocities[:,2]=velocities[:,2]+acc[:,2]*dt        

    positions[:,0]=positions[:,0]+velocities[:,0]*dt
    positions[:,1]=positions[:,1]+velocities[:,1]*dt
    positions[:,2]=positions[:,2]+velocities[:,2]*dt

    planets.set_sizes(masses[:]*20)
    planets._offsets3d(positions[:,0],positions[:,1],positions[:2])

ani=FuncAnimation(fig,animate,frames=1000,interval=1,blit=False)

When I run it, I get the following error message:

Traceback (most recent call last):

File "<ipython-input-30-b2a4f400dbe9>", line 1, in <module>
runfile('C:/Program Files (x86)/WinPython-64bit-3.3.5.9/python-3.3.5.amd64/Scripts/orbit/3dnbody_2dprojection.py', wdir='C:/Program Files (x86)/WinPython-64bit-3.3.5.9/python-3.3.5.amd64/Scripts/orbit')

File "C:\Program Files (x86)\WinPython-64bit-3.3.5.9\python-3.3.5.amd64\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 685, in runfile
execfile(filename, namespace)

File "C:\Program Files (x86)\WinPython-64bit-3.3.5.9\python-3.3.5.amd64\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 85, in execfile
exec(compile(open(filename, 'rb').read(), filename, 'exec'), namespace)

File "C:/Program Files (x86)/WinPython-64bit-3.3.5.9/python-3.3.5.amd64/Scripts/orbit/3dnbody_2dprojection.py", line 56, in <module>
ani=FuncAnimation(fig,animate,frames=1000,interval=1,blit=False)

File "C:\Program Files (x86)\WinPython-64bit-3.3.5.9\python-3.3.5.amd64\lib\site-packages\matplotlib\animation.py", line 1067, in __init__
TimedAnimation.__init__(self, fig, **kwargs)

File "C:\Program Files (x86)\WinPython-64bit-3.3.5.9\python-3.3.5.amd64\lib\site-packages\matplotlib\animation.py", line 913, in __init__
*args, **kwargs)

File "C:\Program Files (x86)\WinPython-64bit-3.3.5.9\python-3.3.5.amd64\lib\site-packages\matplotlib\animation.py", line 591, in __init__
self._init_draw()

File "C:\Program Files (x86)\WinPython-64bit-3.3.5.9\python-3.3.5.amd64\lib\site-packages\matplotlib\animation.py", line 1092, in _init_draw
self._draw_frame(next(self.new_frame_seq()))

File "C:\Program Files (x86)\WinPython-64bit-3.3.5.9\python-3.3.5.amd64\lib\site-packages\matplotlib\animation.py", line 1106, in _draw_frame
self._drawn_artists = self._func(framedata, *self._args)  

File "C:/Program Files (x86)/WinPython-64bit-3.3.5.9/python-3.3.5.amd64/Scripts/orbit/3dnbody_2dprojection.py", line 54, in animate
planets._offsets3d(positions[:,0],positions[:,1],positions[:2])

TypeError: 'tuple' object is not callable

As far as I can tell, I am not calling a tuple (from my understanding this would be trying to do e.g. F(2) instead of F[2], where F is a tuple), which is why this is both confusing and frustrating for me.

I also welcome any hot tips of how I could make this code more elegant/efficient.

Ralf
  • 16,086
  • 4
  • 44
  • 68
Adam K
  • 13
  • 2

1 Answers1

0

It seems the error is from this line

planets._offsets3d(positions[:,0],positions[:,1],positions[:2])

According to this answer and this answer, _offsets3d is a private undocumented attribute that is a tuple that contains coordinates, meaning it is not callable.

Why are you trying to call _offsets3d like function?

If you are trying to set the values, maybe use this:

planets._offsets3d = (positions[:,0], positions[:,1], positions[:2])
Ralf
  • 16,086
  • 4
  • 44
  • 68
  • Hi, thanks for your answer. I got the idea from this post - stackoverflow.com/questions/41602588/… -, but it seems that I did not read the code properly. You are right, and changing `planets._offsets3d(positions[:,0],positions[:,1],positions[:2])` to `planets._offsets3d = (positions[:,0],positions[:,1],positions[:2])` fixed it right up. Thank you! – Adam K Oct 20 '18 at 14:05