Intro to the task

我正在模拟火卫一围绕火星的轨道.我已经完成了使用数值积分来更新两个物体的速度和位置的任务.我的最后一个任务是制作火卫一轨道的动画图,火星以其初始位置为中心.

Code snippet

# plot the animated orbit
    def plot_orbit(self):
        # load all the data from the json file
        all_data = self.load_data_for_all_bodies()
        # get the positions and velocities as 2d vectors
        positions, velocities = self.simulate()
    
        # Create a figure and axis
        fig = plt.figure()
        ax = plt.axes()

        mars_initial_pos_x = all_data["Mars"]["initial_position"][0]
        mars_initial_pos_y = all_data["Mars"]["initial_position"][1]
        print("mars: ", mars_initial_pos_x, mars_initial_pos_y)

        phobos_initial_pos_x = all_data["Phobos"]["initial_position"][0]
        phobos_initial_pos_y = all_data["Phobos"]["initial_position"][1]
        print("phobos: ", phobos_initial_pos_x, phobos_initial_pos_y)
        # Set the limits of the axes
        ax.set_xlim(-9377300, 9377300)
        # mars as a red circle
        mars_circle = plt.Circle(
            (mars_initial_pos_x, mars_initial_pos_y),
            4,
            color="red",
            label="Mars",
            animated=True,
        )
        ax.add_patch(mars_circle)

        # Phobos as a blue circle
        phobos_circle = plt.Circle(
            (phobos_initial_pos_x, phobos_initial_pos_y),
            80,
            color="blue",
            label="Phobos",
            animated=True,
        )
        ax.add_patch(phobos_circle)

        # Function to update the position of Phobos
        def animate(frame):
            phobos_circle.center = (positions[frame][0], positions[frame][1])
            return (phobos_circle,)

        # Create the animation
        ani = FuncAnimation(
            fig, animate, frames=self.iteration_counts, interval=20, blit=True
        )

        plt.title(f"Orbit of {self.name}")
        plt.xlabel("x (m)")
        plt.ylabel("y (m)")
        plt.legend()

        plt.show()

Data file(JSON)

{
    "Mars": {
        "mass": 6.4185e+23,
        "initial_position": [
            0,
            0
        ],
        "initial_velocity": [
            0,
            0
        ],
        "orbital_radius": null
    },
    "Phobos": {
        "mass": 1.06e+16,
        "orbital_radius": 9377300.0,
        "initial_position": [
            9377300.0,
            0
        ],
        "initial_velocity": [
            0,
            2137.326983980658
        ]
    }
}

Output

enter image description here

在上面的输出中,我可以看到由于我前面的方法,位置和速度数组已经正确填充.然而,生成的图表是一场彻底的灾难.我在Jupyter笔记本和VS代码中运行了代码,Jupyter笔记本做到了这一点,VS代码没有在屏幕上打印出来.我的第一步是首先在图形上将圆放在正确的位置,然后考虑动画功能.遗憾的是,我在输出中看不到任何圆圈.我使用的代码与在回答有关堆栈上的FuncAnimation的问题时给出的代码非常相似.

如有任何帮助,我们将不胜感激!

Edit

修改后的代码(可执行文件)

# include neccesary libraries
import numpy as np
import matplotlib.pyplot as plt
import json
from matplotlib.animation import FuncAnimation


# define constants
timestep = 0.001
iterations = 1000
G = 6.674 * 10**-11


def main():
    # make the modification
    modify_initial_velocity_of_phobos()

    # Example usage
    mars = Planet("Mars")
    phobos = Planet("Phobos")
    # Display information
    mars.display_info()
    phobos.display_info()

    # mars.acceleration()
    # phobos.acceleration()
    phobos_orbit = Simulate("Phobos", timestep, iterations)
    phobos_orbit.plot_orbit()


# Function to calculate the initial velocity of a moon orbiting a planet
def calculate_orbital_velocity(mass, radius):
    # use the formula given to calculate
    return (G * mass / radius) ** 0.5


# self explanatory function
def modify_initial_velocity_of_phobos():
    # Read data from the JSON file
    with open("planets.json", "r+") as file:
        celestial_data = json.load(file)

        # Extract necessary data for calculation
        mass_of_mars = celestial_data["Mars"]["mass"]
        orbital_radius_of_phobos = celestial_data["Phobos"]["orbital_radius"]

        # Calculate orbital velocity of Phobos
        velocity_of_phobos = calculate_orbital_velocity(
            mass_of_mars, orbital_radius_of_phobos
        )

        # Update the initial_velocity for Phobos in the data
        celestial_data["Phobos"]["initial_velocity"] = [0, velocity_of_phobos]

        # Move the file pointer is back to the start of the file
        file.seek(0)

        # Write the updated data back to the JSON file
        json.dump(celestial_data, file, indent=4)

        # Truncate the file to remove any leftover data
        file.truncate()


# create a class for the planet which calculates the net gravitational force and acceleration due to it acting on it
class Planet():
    def __init__(self, name):
        self.name = name
        body_data = self.load_data_for_main_body()

        # Initialize attributes with data from JSON or default values
        self.mass = body_data.get("mass")
        self.position = np.array(body_data.get("initial_position"))
        self.velocity = np.array(body_data.get("initial_velocity"))
        self.orbital_radius = body_data.get("orbital_radius", 0)

    # load main planet(body) data from the json file
    def load_data_for_main_body(self):
        with open("planets.json", "r") as file:
            all_data = json.load(file)
            body_data = all_data.get(self.name, {})
            return body_data

    # load all data from the json file
    def load_data_for_all_bodies(self):
        with open("planets.json", "r") as file:
            all_data = json.load(file)
            return all_data

    # calculate the gravitational force between two bodies
    def force(self):
        all_bodies = self.load_data_for_all_bodies()
        # initialize the total force vector
        total_force = np.array([0.0, 0.0])
        # iterate over all the bodies
        for body_name, body_data in all_bodies.items():
            if body_name == self.name:
                continue

            # get the position of each body
            other_body_position = np.array(body_data["initial_position"])

            # get the mass of each body
            other_body_mass = body_data["mass"]
            # calculate distance vector between the two bodies
            r = other_body_position - self.position
            # Calculate the distance between the two bodies
            mag_r = np.linalg.norm(r)
            # Normalize the distance vector
            r_hat = r / mag_r
            # Calculate the force vector between the two bodies
            force = (G * self.mass * other_body_mass) / (mag_r**2) * r_hat
            # Add the force vector to the total force vector
            total_force += force
        return total_force

    # calculate the acceleration due to the force
    def acceleration(self):
        # Calculate the force vector
        force = self.force()
        # Calculate the acceleration vector
        acceleration = force / self.mass

        return acceleration

    # update the position of the body using the velocity and time step
    def update_position(self):
        self.position = self.position + self.velocity * timestep
        return self.position

    # update the velocity of the body using the acceleration and time step
    def update_velocity(self):
        self.velocity = self.velocity + self.acceleration() * timestep
        return self.velocity

    def display_info(self):
        print(f"Name: {self.name}")
        print(f"Mass: {self.mass} kg")
        print(f"Position: {self.position} m")
        print(f"Velocity: {self.velocity} m/s")
        if self.orbital_radius is not None:
            print(f"Orbital Radius: {self.orbital_radius} m")
        else:
            print("Orbital Radius: Not applicable")


# define class to simulate the orbit of the moon around the planet
class Simulate(Planet):
    def __init__(self, name, delta_t, iteration_counts):
        super().__init__(name)
        self.delta_t = delta_t
        self.iteration_counts = iteration_counts

    def simulate(self):
        global timestep
        # initialize the arrays to store the positions and velocities as vectors
        positions = np.zeros((self.iteration_counts, 2))
        velocities = np.zeros((self.iteration_counts, 2))
        # iterate over the number of iterations
        for i in range(self.iteration_counts):
            # update the position
            positions[i] = self.update_position()
            # update the velocity
            velocities[i] = self.update_velocity()
            # update time
            timestep += self.delta_t
        print("pos: ", positions)
        print("vel: ", velocities)
        return positions, velocities

    # plot the animated orbit
    def plot_orbit(self):
        # load all the data from the json file
        all_data = self.load_data_for_all_bodies()
        # get the positions and velocities as vectors
        positions, velocities = self.simulate()
        # debug statements
        print("pos: ", positions)
        print("vel: ", velocities)
        # Create a figure and axis
        fig, ax = plt.subplots()

        mars_initial_pos_x = all_data["Mars"]["initial_position"][0]
        mars_initial_pos_y = all_data["Mars"]["initial_position"][1]
        print("mars: ", mars_initial_pos_x, mars_initial_pos_y)

        phobos_initial_pos_x = all_data["Phobos"]["initial_position"][0]
        phobos_initial_pos_y = all_data["Phobos"]["initial_position"][1]
        print("phobos: ", phobos_initial_pos_x, phobos_initial_pos_y)
        # Set the limits of the axes
        ax.set_xlim(-9377300, 9377300)
        # mars as a red circle
        mars_circle = plt.Circle(
            (mars_initial_pos_x, mars_initial_pos_y),
            0.1,
            color="red",
            label="Mars",
            ec = "None"
            
        )
        ax.add_patch(mars_circle)

        # Phobos as a blue circle
        phobos_circle = plt.Circle(
            (phobos_initial_pos_x, phobos_initial_pos_y),
            0.1,
            color="blue",
            label="Phobos",
            ec = "None"
            
        )
        ax.add_patch(phobos_circle)

        # Function to update the position of Phobos
        def animate(frame):
              phobos_circle.center = (positions[frame][0], positions[frame][1])
              ax.draw_artist(phobos_circle)
              return phobos_circle,

        # Create the animation
        ani = FuncAnimation(
            fig = fig, func = animate, frames=self.iteration_counts, interval=20, blit=True
        )



        plt.title(f"Orbit of {self.name}")
        plt.xlabel("x (m)")
        plt.ylabel("y (m)")
        plt.legend()

        plt.show()
        fig.savefig('MyFigure.png', dpi=200)
        # plot the orbit
        # plt.plot(positions[:, 0], positions[:, 1])
        # plt.title(f"Orbit of {self.name}")
        # plt.xlabel("x(m)")
        # plt.ylabel("y(m)")
        # plt.show()


if __name__ == "__main__":
    main()

推荐答案

好的...有了完整的代码,就很容易看到哪里出了问题:-)

主要问题是:

  • 你的圈子太小了!
  • you set an en或mous x-extent but keep the y-extent to 0-1 (since your axis does not preserve the aspect-ratio (e.g. ax.set_aspect("equal")) this means your circles look like lines...
  • 你的动作太慢了
  • 一旦你的main()函数被解析,你的对象就会被垃圾回收,这样动画就不能运行

要修复您的代码,您必须执行以下操作:

Give Mars 和 Phobos a proper radius

        mars_circle = plt.Circle(
            (mars_initial_pos_x, mars_initial_pos_y),
            1e5,
            col或="red",
            label="Mars",
            ec = "None"
            
        )

        # Phobos as a blue circle
        phobos_circle = plt.Circle(
            (phobos_initial_pos_x, phobos_initial_pos_y),
            1e5,
            col或="blue",
            label="Phobos",
            ec = "None",
        )

确保您的轴限制设置正确

要么

        ax.set_xlim(-9377300, 9377300)
        ax.set_ylim(-9377300, 9377300)

        ax.set_xlim(-9377300, 9377300)
        ax.set_aspect("equal")

将间隔时间缩短到你能看到的程度(否则你的动作会非常慢)

        self.ani = FuncAnimation(
            fig = fig, func = animate, frames=self.iteration_counts, interval=.5, blit=True
        )

确保动画对象未被垃圾回收

        # Create the animation
        self.ani = FuncAnimation(
            fig = fig, func = animate, frames=self.iteration_counts, interval=.5, blit=True
        )

def main():
    ...
    ...

    phobos_或bit = Simulate("Phobos", timestep, iterations)
    phobos_或bit.plot_或bit()
    return phobos_或bit

if __name__ == "__main__":
    phobos_或bit = main()

... 和 you apparently don't need an explicit call to ax.draw_artist()...

如果你改变这一切,它似乎做得很好:

animated figure

Python相关问答推荐

我可以随时间更新变量的类型注释吗?

为什么我的主页不会重定向到详细视图(Django)

将词典写入Excel

Pandas read_jsonfuture 警告:解析字符串时,to_datetime与单位的行为已被反对

KNN分类器中的GridSearchCV

使用from_pandas将GeDataFrame转换为polars失败,ArrowType错误:未传递numpy. dype对象

Python中的函数中是否有充分的理由接受float而不接受int?

无法使用equals_html从网址获取全文

优化在numpy数组中非零值周围创建缓冲区的函数的性能

Python:在类对象内的字典中更改所有键的索引,而不是仅更改一个键

TARete错误:类型对象任务没有属性模型'

如何在Python中将returns.context. DeliverresContext与Deliverc函数一起使用?

处理(潜在)不断增长的任务队列的并行/并行方法

从numpy数组和参数创建收件箱

如何获取TFIDF Transformer中的值?

计算组中唯一值的数量

Pandas—合并数据帧,在公共列上保留非空值,在另一列上保留平均值

使用密钥字典重新配置嵌套字典密钥名

使用NeuralProphet绘制置信区间时出错

如何在TensorFlow中分类多个类