numpy 如何在每个时间步动画粒子,而不是他们的轨道在我的模拟?

m2xkgtsf  于 2024-01-08  发布在  其他
关注(0)|答案(1)|浏览(102)

我想以更好的方式设置模拟的动画:
a)目前的代码是模拟它们的轨道作为时间的函数。这真的是一些锯齿形的线在目前这是“丑陋”看。
B)我希望num_particles = 100在每一个时刻都是动画的。就像一个球在空间中移动。我不想跟踪它的轨迹/轨道。
c)所以在每个时间步,我应该只有我的num_particles作为云。
d)保存动画为mp4或类似文件。
我的模拟代码:

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from mpl_toolkits.mplot3d import Axes3D
  4. from matplotlib.animation import FuncAnimation
  5. # Constants
  6. M_Sun = 1.989e30 # Solar Mass
  7. M_bh = 4.3e6 *M_Sun #Mass of Sgr A*
  8. G = 6.67430e-11 # m^3 kg^(-1) s^(-2)
  9. yr = 365 * 24 * 60 * 60 # 1 year in seconds
  10. R = 6.371e3 #Radius of Sphere
  11. # Number of particles
  12. num_particles = 100
  13. #Uniformly distributed particles in Sphere of radius R
  14. phi = np.random.uniform(0, 2*np.pi, size=num_particles)
  15. costheta = np.random.uniform(-1, 1, size=num_particles)
  16. u = np.random.uniform(0, 1, size=num_particles)
  17. theta = np.arccos(costheta)
  18. r = R * (u**(1/3))
  19. x = r * np.sin(theta) * np.cos(phi)
  20. y = r * np.sin(theta) * np.sin(phi)
  21. z = r * np.cos(theta)
  22. # Initial conditions for the particles (m and m/s)
  23. initial_pos = np.random.uniform(0.8e13, 1.1e14, (num_particles, 3))
  24. initial_vel = np.random.uniform(550e3, 730e3, (num_particles, 3))
  25. # Steps
  26. t_end = 1*yr # Total time of integration
  27. dt_constant = 0.1
  28. intervals = 1000000 # seconds after which to store pos & vel
  29. next_interval_time = intervals
  30. # Arrays to store pos and vel
  31. pos = np.zeros((num_particles, 1, 3)) # shape: (#particles, timesteps taken, #coordinates)
  32. vel = np.zeros((num_particles, 1, 3)) # shape: (#particles, timesteps taken, #coordinates)
  33. # Initial conditions
  34. time = np.zeros(1) # time array
  35. pos[:, 0] = initial_pos
  36. vel[:, 0] = initial_vel
  37. pos_output = []
  38. vel_output = []
  39. t_output = []
  40. while time[-1] <= t_end: # NOTE: We end up doing one more timestep after t_end
  41. r = np.linalg.norm(pos[:, -1], axis=1)
  42. acc = -G * M_bh / r[:, np.newaxis]**3 * pos[:, -1] #np.newaxis for broadcasting with pos[:, i-1]
  43. # Calculate the time step for the current particle
  44. current_dt = dt_constant * np.sqrt(np.linalg.norm(pos[:, -1], axis=1)**3 / (G * M_bh))
  45. min_dt = np.min(current_dt) # Use the minimum time step for all particles
  46. # leap frog integration
  47. # calculate velocity at half timestep
  48. half_vel = vel[:, -1] + 0.5 * acc * min_dt
  49. # current position only used in this timestep
  50. _pos_t = pos[:, -1] + half_vel * min_dt # shape: (#particles, #coordinates)
  51. # Recalculate acceleration with the new position
  52. r = np.linalg.norm(_pos_t, axis=1)
  53. # Acceleration at timestep
  54. acc = -G * M_bh / r[:, np.newaxis]**3 * _pos_t #np.newaxis for broadcasting with pos[:, i-1]
  55. # current velocity only used in this timestep
  56. _vel_t = half_vel + 0.5 * acc * min_dt # shape: (#particles, #coordinates)
  57. # time at timestep t
  58. _time_t = time[-1] + min_dt
  59. # Check if the elapsed time has surpassed the next interval
  60. if _time_t >= next_interval_time:
  61. pos_output.append(_pos_t.copy())
  62. vel_output.append(_vel_t.copy())
  63. t_output.append(_time_t.copy())
  64. next_interval_time += intervals
  65. # add axis at position 1 to allow concatenation
  66. pos = np.concatenate((pos, _pos_t[:, np.newaxis]), axis=1)
  67. vel = np.concatenate((vel, _vel_t[:, np.newaxis]), axis=1)
  68. time = np.append(time, _time_t)
  69. # show current status by printing timestep number (-1 because initial conditions)
  70. print(f'timestep: {time.size -1} [progress: {_time_t/t_end*100:.3f}%]')
  71. pos_output = np.array(pos_output).transpose((1, 0, 2)) # shape: (#objects, #stored timesteps, #coordinates)
  72. np.save('pos_output.npy', pos_output)
  73. vel_output = np.array(vel_output).transpose((1, 0, 2)) # shape: (#objects, #stored timesteps, #coordinates)
  74. np.save('vel_output.npy', vel_output)
  75. t_output = np.array(t_output)
  76. np.save('t_output.npy', t_output)
  77. print(pos_output.shape)
  78. print(vel_output.shape)
  79. print(t_output.shape)
  80. #ANIMATION
  81. from orbit_animation import animate_orbits
  82. animate_orbits(pos_output)

字符串
我的动画代码:

  1. # orbit_animation.py
  2. import numpy as np
  3. import matplotlib.pyplot as plt
  4. from mpl_toolkits.mplot3d import Axes3D
  5. from matplotlib.animation import FuncAnimation
  6. def animate_orbits(pos, intervals=1000000, interval=500):
  7. fig = plt.figure(figsize=(8, 8))
  8. ax = fig.add_subplot(111, projection='3d')
  9. # Scatter plot for Sgr A*
  10. sgr_a_plot = ax.scatter([0], [0], [0], color='black', marker='o', s=50, label='Sgr A*')
  11. # Initialize an empty line for the cloud particles
  12. cloud_plot, = ax.plot([], [], [], label='Cloud Particles')
  13. # Set plot labels and title
  14. ax.set_xlabel('X (km)')
  15. ax.set_ylabel('Y (km)')
  16. ax.set_zlabel('Z (km)')
  17. ax.legend(loc='upper right', bbox_to_anchor=(1.1, 1.1))
  18. ax.set_title('Orbits of cloud particles around Sgr A*')
  19. # Initialize axis limits
  20. x_min, x_max = np.min(pos[:, :, 0]), np.max(pos[:, :, 0])
  21. y_min, y_max = np.min(pos[:, :, 1]), np.max(pos[:, :, 1])
  22. z_min, z_max = np.min(pos[:, :, 2]), np.max(pos[:, :, 2])
  23. # Animation update function
  24. def update(frame):
  25. # Update Sgr A* position
  26. sgr_a_plot._offsets3d = ([0], [0], [0])
  27. # Update cloud particles
  28. cloud_plot.set_data(pos[:, frame, 0], pos[:, frame, 1])
  29. cloud_plot.set_3d_properties(pos[:, frame, 2])
  30. # Update axis limits dynamically
  31. x_min, x_max = np.min(pos[:, :, 0]), np.max(pos[:, :, 0])
  32. y_min, y_max = np.min(pos[:, :, 1]), np.max(pos[:, :, 1])
  33. z_min, z_max = np.min(pos[:, :, 2]), np.max(pos[:, :, 2])
  34. ax.set_xlim(x_min, x_max)
  35. ax.set_ylim(y_min, y_max)
  36. ax.set_zlim(z_min, z_max)
  37. return sgr_a_plot, cloud_plot
  38. # Create the animation
  39. animation = FuncAnimation(fig, update, frames=pos.shape[1], interval=interval, blit=True)
  40. plt.show()


我希望我能解释我在这里试图实现什么!提前感谢您的帮助!

6vl6ewon

6vl6ewon1#

你已经很接近了。
若要移除连接粒子的线,请将linestyle设置为"none"。若要绘制圆形标记,请将marker设置为o

  1. cloud_plot, = ax.plot([], [], [], linestyle="none", marker='o', label='Cloud Particles')

字符串
interval=500(iidoee. 0. 5 fps)使动画非常起伏,我会把这个数字减少到50(iidoee. 20 fps)。
要将动画保存为mp4格式,只需将文件路径的扩展名设为.mp4即可。将保存文件的fps与动画匹配是有意义的。

  1. animation.save("test.mp4", fps=20)

相关问题