I want to animate the astrofisical two-body-problem with mass loss on python. In doing so I want also that my orbit is not a rigid line but it fades gradually as the frame updates. In doing so I had to move away from the usual line = ax.plot(....)
but instead use a collection of lines.
The issue comes up when we return multiple objects from the update function that can be overcome easily in the first case by substituiting the definition of lines with a coma like so: lines, = ax.plot(....)
. However, if I do the same an ValueError is raised: too many values to unpack (expected 1).
Here’s my code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.collections import LineCollection
import matplotlib.colors as mcolors
def color_name_to_rgba(color_name, alpha):
# Get the RGB tuple for the given color name
rgb = mcolors.to_rgb(color_name)
# Construct the RGBA tuple
rgba = (*rgb, alpha)
return rgba
n_bodies = 2
file_path = 'C:\Alez\università\Astrophysics_and_Cosmology\2_year\First_Period\Computational_Astrophysics\Project'
positions = []
pos = []
img = plt.imread(file_path + '\MW.jpg')
for i in range(n_bodies):
file_name = f'\BinarySystem1_SN_output1_body{i+1}.txt'
X = np.genfromtxt(file_path + file_name)
positions.append(X)
for i in range(n_bodies):
file_name = f'\BinarySystem1_SN_output2_body{i+1}.txt'
X = np.genfromtxt(file_path + file_name)
pos.append(X)
positions = np.array(positions)
pos = np.array(pos)
t_life = 6.22 #Myr
time_per_frame = t_life / np.shape(pos)[1]
# Create a figure and axis
def TwoDanim(pos, bg,
xlim = None,
ylim = None,
orbit_colors = ('white', 'pink'),
fading = False,
fading_length = 1000,
orbit_alpha = 1.,
orbit_linewidth = 1.,
framerate = 0.1,
blit = True,
time_simulation = None):
fig, ax = plt.subplots()
ax.set_aspect('equal', 'box')
ax.set_xlabel('x [nbody units]')
ax.set_ylabel('y [nbody units]')
ax.spines['bottom'].set_color('white')
ax.spines['top'].set_color('white')
ax.spines['right'].set_color('white')
ax.spines['left'].set_color('white')
fig.patch.set_facecolor('black')
ax.tick_params(axis = 'x', colors = 'white')
ax.tick_params(axis = 'y', colors = 'white')
ax.yaxis.label.set_color('white')
ax.xaxis.label.set_color('white')
ax.imshow(bg, extent=list(xlim)+list(ylim))
lines, = [LineCollection([], linewidths = orbit_linewidth) for _ in range(pos.shape[0])]
for line in lines:
ax.add_collection(line)
time_text = ax.text(0.02, 0.95, '', transform = ax.transAxes, color = 'white')
def init():
for line in lines:
line.set_segments([])
line.set_colors([])
time_text.set_text('')
return lines, time_text
def update(frame):
for i, line in enumerate(lines):
segments = []
colors = []
if fading:
start_frame = max(0, frame - fading_length)
for k in range(start_frame, frame):
alpha = max(0, orbit_alpha / fading_length * k - (frame - fading_length) / fading_length)
color = color_name_to_rgba(orbit_colors[i], alpha)
segments.append([(pos[i, k, 0], pos[i, k, 1]), (pos[i, k+1, 0], pos[i, k+1, 1])])
colors.append(color)
line.set_segments(segments)
line.set_colors(colors)
else:
line.set_data(pos[i, :frame, 0], pos[i, :frame, 1])
color = color_name_to_rgba(orbit_colors[i], orbit_alpha)
line.set_color(color)
time_passed = time_simulation * frame / n_frames
time_text.set_text(f'Time passed: {time_passed:.2f} Myr')
return lines, time_text
n_frames = pos.shape[1]
ani = FuncAnimation(fig, update, frames = n_frames, init_func = init, blit = blit, interval = framerate)
plt.show()
# Example usage
xlim = (np.min(positions[:,:,0]) * (1 + 0.1), np.max(positions[:,:,0]) * (1 + 0.1))
ylim = (np.min(positions[:,:,1]) * (1 + 0.1), np.max(positions[:,:,1]) * (1 + 0.1))
TwoDanim(pos, img, fading = True, orbit_colors = ("white", "gray"), orbit_alpha = 1, fading_length = 200, xlim = xlim, ylim = ylim, time_simulation = t_life)
Alex Helix Pozzobon is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.