2

Let me preface this by letting everyone know that I'm still somewhat new to programming, so I'm sure my code's probably fairly inefficient in some parts with some potentially cringeworthy lines. I apologize in advance.

Anyways, I'm trying to program a 2D physics simulator that simulates the interaction between two celestial bodies given a set of initial conditions, and then animates their trajectories. The problem is, when I run my code, I get the following error: AttributeError: 'list' object has no attribute 'set_data', specifically citing the line.set_data([],[]) command in init(). This error points at line 61, which I've marked below (I cut out some unnecessary comments & definitely messed up the line numbers for the code snippet you're seeing below). Here is my code:

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

# ->> Initialize Figure <<-
fig = plt.figure()                                  # creates figure window
ax = plt.axes(xlim=(x[0],x[-1]),ylim=(y[0],y[-1]))  # creates axis object
plotColors = ("blue","red","green","black") # colors to use in plots (qty. must match or exceed numBodies)
line, = ax.plot([],[],lw=2)                 # creates blank line object
lines = []                                  # list that contains line objects for simulated bodies
numBodies = 2                               # number of bodies to intitialize (not necessarily simulate)
numArgs = 2                                 # DO NOT CHANGE: number of bodies to simulate
xBody1,xBody2 = [],[]                       # tracks x-coordinates for both simulated bodies
yBody1,yBody2 = [],[]                       # tracks y-coordinates for both simulated bodies

print("-- One --")

# ->> Initialize list of line objects for all bodies <<-
for i in range(numArgs):
    line_obj = ax.plot([],[],lw=2,color=plotColors[i])
    lines.append(line_obj)

print("-- Two --")

# ->> Initialize Animation Frame Initialization Function <<-
def init():
    for line in lines:
        line.set_data([],[])  #............................................<<<<<<< LINE 61 <<<<<<<
    return lines

print("-- Three --")

# ->> Initialize Animation Function (called sequentially) <<-
def animate(i):
        
    xList = [xBody1,xBody2]     # contains x-coordinate data for each body
    yList = [yBody1,yBody2]     # contains y-coordinate data for each body
    
    for num,line in enumerate(lines):           # for index in range(0,1):
        line.set_data(xList[num],yList[num])    # set data for each line separately
    
    plt.pause(0.1)
    return lines

# ->> Initialize Numerical Calculation Sequence <<-
def calculate(lastP,lastV,lastA):               # calculates each iteration of movements
    x1 = lastP[0] + lastV[0]*dt + 0.5*lastA[0]*np.square(dt)
    y1 = lastP[1] + lastV[1]*dt + 0.5*lastA[1]*np.square(dt)
    x2 = lastP[2] + lastV[2]*dt + 0.5*lastA[2]*np.square(dt)
    y2 = lastP[3] + lastV[3]*dt + 0.5*lastA[3]*np.square(dt)
    vx1 = lastV[0] + lastA[0]*dt
    vy1 = lastV[1] + lastA[1]*dt
    vx2 = lastV[2] + lastA[2]*dt
    vy2 = lastV[3] + lastA[3]*dt
    fx1 = G*m1*m2/np.square(x2-x1)
    fy1 = G*m1*m2/np.square(y2-y1)
    fx2 = G*m1*m2/np.square(x1-x2)
    fy2 = G*m1*m2/np.square(y1-y2)
    ax1 = fx1/m1
    ay1 = fy1/m1
    ax2 = fx2/m2
    ay2 = fy2/m2
    pos = [x1,y1,x2,y2]
    vel = [vx1,vy1,vx2,vy2]
    force = [fx1,fy1,fx2,fy2]
    acc = [ax1,ay1,ax2,ay2]
    return pos,vel,force,acc

# ->> Initialize Simulation Function
def simulate(sPos,sVel,sAcc):     # handles calculations & data management for animation
    xx1,xx2 = [],[]
    yy1,yy2 = [],[]
    xx1.append(sPos[0])
    yy1.append(sPos[1])
    xx2.append(sPos[2])
    yy2.append(sPos[3])
    Pos,Vel,Force,Acc = calculate(sPos,sVel,sAcc)
    for t in range(N):
        lastPos = Pos
        lastVel = Vel
        lastAcc = Acc
        Pos,Vel,Force,Acc = calculate(lastPos,lastVel,lastAcc)
        xx1.append(Pos[0])
        yy1.append(Pos[1])
        xx2.append(Pos[2])
        yy2.append(Pos[3])
    return xx1,yy1,xx2,yy2

print("-- Four --")

# ->> Specify Simulation Quantities <<-
G = 1           # gravitational constant (actually equals 6.67430e-11)
tmin = 0
tmax = 10000
N = 20000
dt = (tmax-tmin)/N

# ->> Specify Initial Conditions <<-
m1 = 1000       # mass of body 1 (kg)
m2 = 1000       # mass of body 2 (kg)

x1s = 10        # starting x-coordinate of body 1
y1s = 90        # starting y-coordinate of body 1
x2s = 90        # starting x-coordinate of body 2
y2s = 10        # starting y-coordinate of body 2

fx1s = 0    # initial x-directed force on body 1 (N) at t=0-
fy1s = 0    # initial y-directed force on body 1 (N) at t=0-
fx2s = 0    # initial x-directed force on body 2 (N) at t=0-
fy2s = 0    # initial y-directed force on body 2 (N) at t=0-

ax1s = fx1s/m1  # initial x-acceleration of body 1 (m/s^2)
ay1s = fy1s/m1  # initial y-acceleration of body 1 (m/s^2)
ax2s = fx2s/m2  # initial x-acceleration of body 2 (m/s^2)
ay2s = fy2s/m2  # initial y-acceleration of body 2 (m/s^2)

vx1s = 0        # initial x-velocity of body 1 (m/s)
vy1s = 0        # initial y-velocity of body 1 (m/s)
vx2s = 0        # initial x-velocity of body 2 (m/s)
vy2s = 0        # initial y-velocity of body 2 (m/s)

# ->> Initialize Physics Vectors <<-
mass = [m1,m2]
Pos = [x1s,y1s,x2s,y2s]
xPos = [x1s,x2s]
yPos = [y1s,y2s]
Force = [fx1s,fy1s,fx2s,fy2s]
xForce = [fx1s,fx2s]
yForce = [fy1s,fy2s]
Acc = [ax1s,ay1s,ax2s,ay2s]
xAcc = [ax1s,ax2s]
yAcc = [ay1s,ay2s]
Vel = [vx1s,vy1s,vx2s,vy2s]
xVel = [vx1s,vx2s]
yVel = [vy1s,vy2s]

simulate(Pos,Vel,Acc)

print(type(line))
print("-- Five --")

# ->> ANIMATE SIMULATION <<-
anim = animation.FuncAnimation(fig, animate, init_func=init,
                               frames=200, interval=20, blit=True)
#anim.save('basic_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
plt.show()

Here's what's messing me up: I know this error is usually given when you "fail to unpack the list of Line2D object", as mentioned in the answer to this question. To solve this, you should include a comma next to the variable you're trying to "unpack". In this case, when I initialized line towards the top of my code snippet, I definitely included that comma. So I'm very confused as to why I'm getting this error message? For reference, I've been following this example for creating the animated plot, and I believe I've been consistent with their code for the most part.

Moreover, I created a few print() statements to help me see various stages of the code's execution. This is what the output looks like before the error message:

-- One --
-- Two --
-- Three --
-- Four --
<class 'matplotlib.lines.Line2D'>
-- Five --

As you can see, the line variable is of type lines.Line2D right before the simulation begins, which should be exactly what we want (right?). My suspicion is that there's something weird going on under the hood once simulation() is called, but I'm not competent enough to navigate this process.

Can anyone help me figure out what's going wrong in my code? Thanks.

Jacob M
  • 147
  • 3
  • 10
  • 1
    You don't include the comma in the for loop when you initialise the other lines – DavidG Jun 24 '21 at 08:32
  • So, you need `line_obj, = ax.plot(...)` because `ax.plot` always returns a list of line objects, although usually only one line object is involved. – JohanC Jun 24 '21 at 11:04

0 Answers0