예제 #1
0
    def plot_orbit(self, r_a_initial, v_a_initial, n_orbits=100):
        """
        Plotting routine for a standard, two picture, orbital plot.
        :param r_a_initial: (list) Initial position vector of the asteroid.
        :param v_a_initial: (list) Initial velocity vector of the asteroid
        :param n_orbits: (int) Number of orbits of Jupiter to plot.
        :return: None
        """

        # Define asteroid and simulate its trajectory for n_orbits orbits of Jupiter
        asteroid = Asteroid(r_a_initial, v_a_initial)
        t, r_a, v_a = asteroid.solve_orbit(n_orbits)

        # Plot overview of orbit (axis 1)
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=[3.0 * 4, 2.8 * 2])
        ax1.set_xlim((-7, 7))
        ax1.set_ylim((-7, 7))
        ax1.plot(r_a[0], r_a[1])
        self.plot_extras(ax1)
        ax1.set_xlabel("x /AU")
        ax1.set_ylabel("y /AU")

        # Plot zoomed in view of asteroid orbit (axis 2)
        ax2.plot(r_a[0], r_a[1])
        ax2.set_xlabel("x /AU")
        ax2.set_ylabel("y /AU")

        # Plot starting position on axis 2
        ax2.plot(r_a[0][0], r_a[1][0], 'r+')

        # Save figure
        plt.savefig("fig.png")
예제 #2
0
def pooled_process_velocity(args):
    """
    Evaluate wander based on input list of initial velocity values.
    :param args: (list) List containing initial conditions of the form (v_x, v_y, r_lagrange_point). Each item is to be
                    evaluated and have its wander calculated.
    :return: (list) List of calculated ranges of wander in the same order as the input list.
    """
    # Iterate through each item in args, calculate wander and append to r_max_list
    r_max_list = []
    for item in args:
        # Save start time of function
        time_start = time()

        # Unpack item and set initial conditions
        x, y, r_lagrange_point = item
        r_a_initial = [r_lagrange_point[0], r_lagrange_point[1], 0]
        v_a_initial = [x, y, 0]

        # Simulate asteroid for 100 orbits
        asteroid = Asteroid(r_a_initial, v_a_initial)
        t, r_a, v_a = asteroid.solve_orbit(100)

        # Calculate r_max, the maximum distance of the asteroid from the Lagrange point
        r_max = np.amax(np.sqrt((r_a[0] - r_lagrange_point[0]) ** 2 + (r_a[1] - r_lagrange_point[1]) ** 2))
        r_max_list.append(r_max)

        # Output runtime for each loop to provide feedback during long calculation
        print(f"Took {time()-time_start}s")

    return r_max_list
예제 #3
0
    def evaluate_mass_wander(self):
        """
        Evaluate wander of an asteroid from L4 for a range of planetary masses.
        :return: None.
        """
        # Define initial velocity vector
        v_a_initial = [0, 0, 0]

        # Populate an array of masses to iterate over
        # masses = np.linspace(0.0001, 0.05, num=100) * constants.MASS_SUN
        masses = np.logspace(-5, -2, num=100)

        # Evaluate masses between a given range of solar masses.
        r_max_list = []
        for mass in masses:
            # Recalculate Lagrange point (L4) position
            r_lagrange = [
                constants.R * ((constants.MASS_SUN - mass) /
                               (constants.MASS_SUN + mass)) *
                np.cos(np.pi / 3), constants.R * np.sin(np.pi / 3), 0
            ]

            # Perturb Lagrange point
            # r_lagrange = np.array(r_lagrange) + np.array([-0.0006 * constants.R, +0.0006 * constants.R, 0])

            # Define a new asteroid with specified planet mass
            asteroid = Asteroid(r_lagrange, v_a_initial, planet_mass=mass)

            # Simulate asteroid over 500 orbits of Jupiter
            t, r_a, v_a = asteroid.solve_orbit(500)

            # Find range of wander r_max
            r_max = np.amax(
                np.sqrt((r_a[0] - r_lagrange[0])**2 +
                        (r_a[1] - r_lagrange[1])**2))

            # Append range of wander to list to be plotted
            r_max_list.append(r_max)

            # Print output as loop executes for debugging
            print(r_max, mass)

        # Open file and dump results to it for future evaluation
        with open("out.txt", "w") as f:
            f.write(
                json.dumps({
                    "r_max_list": r_max_list,
                    "masses": masses.tolist()
                }))
예제 #4
0
    def animate(self):
        """
        Animation routine for a standard orbit, slightly perturbed from L4. Used to create "animation1.mp4".
        :return: None.
        """
        # Define initial conditions
        r_a_initial = [
            constants.R * ((constants.MASS_SUN - constants.MASS_JUPITER) /
                           (constants.MASS_SUN + constants.MASS_JUPITER)) *
            np.cos(np.pi / 3), constants.R * np.sin(np.pi / 3), 0
        ]
        v_a_initial = [0, 0, 0]
        r_a_initial = np.array(r_a_initial) + np.array(
            [-0.001 * constants.R, +0.001 * constants.R, 0])

        # Create asteroid object and simulate for 60 orbits
        asteroid = Asteroid(r_a_initial, v_a_initial)
        t, r_a, v_a = asteroid.solve_orbit(60)

        # Create figure and subplots
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=[3.2 * 4, 2.8 * 2])

        # Set axis limits and label axes
        ax1.set_xlim((-7, 7))
        ax1.set_ylim((-7, 7))
        ax2.set_xlim((np.min(r_a[0]), np.max(r_a[0])))
        ax2.set_ylim((np.min(r_a[1]), np.max(r_a[1])))
        ax1.set_xlabel("x /AU")
        ax1.set_ylabel("y /AU")
        ax2.set_xlabel("x /AU")
        ax2.set_ylabel("y /AU")

        # Plot Sun and Jupiter
        self.plot_extras(ax1)

        # Define variables to plot asteroid loci
        line, = ax1.plot([], [])
        line_zoomed, = ax2.plot([], [])
        point, = ax1.plot([], [], "ro", markersize=3)

        # Define graphic to show orbit location
        sun_circle = plt.Circle((-6, 6), 0.15, color="k")
        radius_line = plt.Circle((-6, 6),
                                 0.8,
                                 color="k",
                                 fill=False,
                                 linewidth=0.2)
        ax1.add_artist(sun_circle)
        ax1.add_artist(radius_line)
        text = ax1.text(-4.8, 6, "t = 0 Yr")

        # Define orbit properties for animation
        omega = np.sqrt(constants.G *
                        (constants.MASS_SUN + constants.MASS_JUPITER) /
                        constants.R**3)
        period = 2 * np.pi / omega
        initial_angle = np.pi / 2  # Additional phase to make graphic plot match large plot at t=0

        # Define animation function
        def animate(i):
            line.set_data(r_a[0][0:i], r_a[1][0:i])
            line_zoomed.set_data(r_a[0][0:i], r_a[1][0:i])
            time = t[i]
            angle = ((time % period) / period) * 2 * np.pi + initial_angle
            x = -6 + 0.8 * np.sin(angle)
            y = 6 + 0.8 * np.cos(angle)
            point.set_data(x, y)
            text.set_text(f"t = {int(t[i])} Yr")
            return line, line_zoomed, point, text

        # Calculate interval and number of frames needed
        FPS = 60.0  # Frames per second in Hz
        ANIM_LENGTH = 20.0  # Animation length in seconds
        interval = 1 / FPS
        frames = int(ANIM_LENGTH * FPS)

        # Define animation
        animation = matplotlib.animation.FuncAnimation(fig,
                                                       animate,
                                                       frames=frames,
                                                       interval=interval,
                                                       blit=True)

        # Save animation
        plt.rcParams['animation.ffmpeg_path'] = constants.FFMPEG_PATH
        FFWriter = matplotlib.animation.writers['ffmpeg']
        writer = FFWriter(
            fps=FPS,
            metadata=dict(artist='Cambridge Computing Project 2020'),
            bitrate=2000)
        animation.save('animation1.mp4', writer=writer)
예제 #5
0
    def evaluate_energy_conservation(self, r_a_initial, v_a_initial, n_orbits):
        """
        Test energy conservation for a given number of orbits and initial conditions.
        :param r_a_initial: (list) Initial position vector of the asteroid.
        :param v_a_initial: (list) Initial velocity vector of the asteroid
        :param n_orbits: (int) Number of orbits of Jupiter to plot.
        :return: None.
        """
        # Define asteroid object with initial conditions
        asteroid = Asteroid(r_a_initial, v_a_initial)

        # Simulate orbit for n_orbits
        t, r_a, v_a = asteroid.solve_orbit(n_orbits)

        # Transpose r_a and v_a for future convenience
        r_a = np.transpose(r_a)
        v_a = np.transpose(v_a)

        # Define omega and orbital period
        omega = np.sqrt(constants.G *
                        (constants.MASS_SUN + constants.MASS_JUPITER) /
                        constants.R**3)
        period = 2 * np.pi / omega

        # Iterate over each time sample and calculate total energy
        energy_list = []
        for i in range(len(t)):
            # Define vectors for potential calculation
            r_a_to_s = self.r_s - r_a[i]
            r_a_to_j = self.r_j - r_a[i]
            r = np.linalg.norm(r_a[i])

            # Calculate gravitational potential energy
            graviatational_potential = -constants.G * (
                constants.MASS_SUN / np.linalg.norm(r_a_to_s) +
                constants.MASS_JUPITER / np.linalg.norm(r_a_to_j))

            # Calculate kinetic energy due to rotating frame
            kinetic_energy_rot = 0.5 * r**2 * omega**2

            # Calculate kinetic energy in the rotating frame
            kinetic_energy_frame = 0.5 * np.linalg.norm(v_a[i])**2

            # Calculate total kinetic energy
            kinetic_energy = -kinetic_energy_rot + kinetic_energy_frame

            # Append total energy to energy_list
            energy_list.append(kinetic_energy + graviatational_potential)

        fig, ax1 = plt.subplots()

        # Plot all energies as a percentage deviation from initial energy
        ax1.plot(t / period,
                 (np.array(energy_list) / energy_list[0] - 1) * 100)

        # Plot expected theoretical energy line
        ax1.hlines(0, np.min(t), np.max(t / period))

        # Set axis labels
        ax1.set_xlabel("Number of orbits /No Units")
        ax1.set_ylabel("|Percentage deviation from expected energy| /%")

        # Save plot
        plt.savefig("fig.png")