Simulating Exoplanet Discoveries with Python

Author:Murphy  |  View: 22793  |  Time: 2025-03-22 23:42:57

Quick Success Data Science

Venus transiting the sun in June 2012 (Evan Clark via Real-world Python)

Before I flew to Idaho to photograph the Great American Eclipse of 2017, I did my homework. The totality event, when the moon completely covered the sun, lasted only 2 minutes and 10 seconds. That left no time for experimenting, testing, or figuring things out on the fly.

To successfully capture images of the penumbra, umbra, solar flares, and diamond ring effect, I had to know exactly what equipment to take, what camera settings to use, and when these events would occur. With the help of the internet, I was able to work this out and prepare a precise timetable for my location.

The diamond ring effect during the 2017 total solar eclipse (by the author)

Similarly, computer simulations prepare scientists for making observations of the natural world. They help them understand what to expect and when to expect it, and how to calibrate their instruments and design their experiments.

The goal of this article is to showcase the practical applications of simulations using exoplanet transit events. Exoplanets are celestial bodies that orbit stars beyond our solar system.

Astronomers have discovered thousands of Exoplanets using a technique called transit photometry, which records the slight dimming of a star's light as an exoplanet passes between the star and Earth. We can use a transit simulator to understand the impact of factors like a planet's size and the presence of sunspots, asteroid fields, moons, and even alien megastructures.

To build the simulator, we'll use OpenCV, Python's premier open-source library for working with images and videos, and Tkinter, Python's built-in tool for building graphical user interfaces (GUIs). We'll use the latter to make a dashboard. Here's a preview:

The Exoplanet Transit Dashboard in action (by the author)

Transit Photometry

In astronomy, a transit occurs when a relatively small celestial body passes directly between the disc of a larger body and an observer. When the small body moves across the face of the larger body, the larger body dims slightly. The best-known transits are those of Mercury and Venus against our sun.

With today's technology, astronomers can detect the subtle dimming of a faraway star's light during a transit event. The technique, called transit photometry, outputs a plot of a star's brightness over time.

The transit photometry technique for detecting exoplanets (from Real-world Python)

In the previous figure, the blue dots on the light curve graph represent measurements of the light given off by a star. When a planet is not positioned over the star (position 1 in the figure), the measured brightness is at a maximum. (We'll ignore light reflected off the exoplanet as it goes through its phases, which would very slightly increase the apparent brightness of the star).

As the leading edge of a planet moves onto the disc (position 2), the emitted light progressively dims, forming a ramp in the light curve. When the entire planet is visible against the disc (position 3), the light curve flattens, and it remains flat until the planet begins exiting the far side of the disc. This creates another ramp (position 4), which rises until the planet passes completely off the disc (position 5). At that point, the light curve flattens at its maximum value, as the star is no longer obscured.

Because the amount of light blocked during transit is proportional to the size of the planet's disc, you can calculate the radius of the planet using the following formula:

where Rp is the planet's radius and Rs is the star's radius. Astronomers determine the star's radius using its distance, brightness, and color, which relates to its temperature. Depth refers to the total change in brightness during the transit, as illustrated in the following figure.

"Depth" is the total change in brightness observed in a light curve (from Real-world Python)

The larger the exoplanet, the greater the depth of the light curve.

Of course, these calculations assume that the whole exoplanet, not just part of it, moved over the face of the star. If the exoplanet skims the top or bottom of the star (from our point of view), the result is an "incomplete" and "V-shaped" light curve of limited use.

A partial transit (red arrow) produces a "V-shaped" light curve (by the author)

Observing exoplanets by measuring light curves isn't just for professional astronomers. According to Sky & Telescope magazine, even small 6" scopes can record useful light curves. NASA has even started a citizen scientist program where backyard astronomers help professionals find Jupiter-sized exoplanets.


The Code

The following Python program uses OpenCV to generate a visual simulation of an exoplanet transiting a star, plots the resulting light curve with Matplotlib, and displays the two together in a dashboard.

To generate a light curve, we'll need to be able to measure changes in brightness. We can do this with OpenCV by performing mathematical operations on pixels.

OpenCV is best installed using pip, so if you're an Anaconda user, you'll want it to be the last thing you add to your conda environment. Here's the installation command:

pip install opencv-python

You'll also need an image of our sun, which you can download from this GitHub repository. Just click the link and then press the download icon to the upper right of the image. Store it in the same folder as your Python script.

Click the icon circled in red to download the image from GitHub (NSO/AURA/NSF)

Importing Libraries and Assigning Constants

The following code imports tkinter, for creating a dashboard; matplotlib, for plotting the light curve, matplotlib's backend_tkagg module and FigureCanvasTkAgg class, for integrating between tkinter and matplotlib; and OpenCV (cv2), for displaying the star image and calculating relative brightness for the light curve.

import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import cv2 as cv

IMG_HT, IMG_WIDTH = 400, 500
BLACK_IMG = cv.imread('limb_darkening.png', cv.IMREAD_GRAYSCALE)
EXO_RADIUS = 7
EXO_DX = 3
EXO_START_X = 40
EXO_START_Y = 230
NUM_FRAMES = 145

The BLACK_IMG variable holds an image of our sun, which will serve as a proxy for an alien star. Note that we load it as a grayscale image so that we can measure intensity (brightness) directly from the pixels.

Using an image allows us to capture real-life variations in the brightness of the photosphere. The photosphere is the luminous outer layer of a star that radiates light and heat. Because the temperature of the photosphere falls as the distance from the star's center increases, the edges of a star's disk are cooler and therefore appear dimmer than the center of the star. This effect is known as _limb darkening_ and it has a noticeable effect on light curves.

The constants with names beginning with EXO_ represent parameters related to the exoplanet. These include its radius, speed (DX), and starting coordinates, measured in pixels. The NUM_FRAMES constant determines how long the simulation will run.

Defining a Function to Create the Dashboard

Our dashboard will include both an image and a graph. OpenCV will handle the image, matplotlib the graph, and Tkinter will combine them into a single display. Tkinter does this using a widget called a canvas, which provides a drawing area for purposes such as displaying images, drawing shapes, and creating interactive elements.

def create_dashboard(root):
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 6))
    canvas = FigureCanvasTkAgg(fig, master=root)
    canvas_widget = canvas.get_tk_widget()
    canvas_widget.pack(side=tk.TOP, fill=tk.BOTH, expand=1)

    intensity_samples = []
    exo_start_x = EXO_START_X

    for _ in range(NUM_FRAMES):
        temp_img = BLACK_IMG.copy()
        cv.circle(temp_img, (exo_start_x, EXO_START_Y), EXO_RADIUS, 0, -1)
        intensity = temp_img.mean()
        intensity_samples.append(intensity)
        relative_brightness = calc_rel_brightness(intensity_samples)

        update_image(ax1, temp_img)
        update_light_curve(ax2, relative_brightness, 'k')

        canvas.draw()
        root.update()
        root.after(3)

        exo_start_x += EXO_DX

The code begins by using matplotlib to make two vertically stacked subplots. We then create the canvas using the FigureCanvasTkAgg class, which embeds the canvas in a Tkinter window. We'll display our figure (fig) on this canvas. As part of the backend_tkagg module, FigureCanvasTkAgg facilitates the proper rendering of Matplotlib figures within a Tkinter application.

The master parameter for this class specifies the master widget (or window) that will contain the canvas. In this case, it's the Tkinter root window (root) that we'll create at the end of the program (using root = tk.Tk()).

We next create an empty list to hold the intensity (relative brightness) measurements, and then reassign the x-axis starting point constant to a new variable, exo_start_x. We'll increment this variable by the EXO_DX constant as the program runs to move the exoplanet forward. The larger the value of EXO_DX, the faster it will move.

The simulation is a for loop controlled by the NUM_FRAMES constant. To avoid degrading the input image, we copy it to a temporary image (temp_img) at the start of each loop. Next, we draw a black circle, representing the exoplanet, using the [cv.circle()](https://docs.opencv.org/4.x/d6/d6e/group__imgproc__draw.html#gaf10604b069374903dbd0f0488cb43670) method.

To measure the intensity, we take the mean of the image and append it to the intensity_samples list.

The next few lines draw the dashboard and update its components by calling functions that we will define next. The root.after(3) line introduces a short delay of 3 milliseconds after each iteration. The function ends by incrementing the exoplanet's x coordinate.

Defining a Function to Calculate Relative Brightness

Next, we define a helper function to calculate the relative brightness from the list of intensity samples. The first step is to find the maximum value in the list. The next step returns a new list in which each intensity item is divided by the maximum value, normalizing the results from 0 to 1.

def calc_rel_brightness(intensity_samples):
    max_brightness = max(intensity_samples)
    return [intensity / max_brightness for intensity in intensity_samples]

Defining Functions to Update the Dashboard

Now we define functions for updating the two components of the dashboard with each loop iteration. The first one updates the grayscale image. The second redraws the light curve with the updated intensity_samples list.

def update_image(ax, img):
    ax.clear()
    ax.imshow(img, cmap='gray')
    ax.axis('off')

def update_light_curve(ax, data, color):
    ax.clear()
    ax.plot(data, 
            color=color, 
            linestyle='dashed', 
            linewidth=2, 
            label='Relative Brightness')
    ax.legend(loc='upper center')
    ax.set_title('Relative Brightness vs. Time')

Running the Simulation

The last bit of code calls the Tkinter root window, the function to create the dashboard, and Tkinter's mainloop() function. The latter is the Tkinter event loop that runs the simulation.

if __name__ == "__main__":
    root = tk.Tk()
    root.title("Exoplanet Transit Dashboard")
    create_dashboard(root)
    root.mainloop()

Here's an example of a completed simulation:

The dashboard at the end of a simulation (by the author)

While the effect of the exoplanet's transit on its star's light curve looks dramatic, you're only seeing the topmost fraction of the total brightness. If you replot the curve with the full range of y-values, the planet's impact is barely perceptible.

The light curve plotted with a full y-axis (by the author)

Going forward, we'll work with single transits, but in real life, astronomers capture many transits, if possible. There's a lot of information entangled in a light curve, and by recording multiple transit events, astronomers can determine an exoplanet's orbital parameters, such as the distance between the planet and the star. They can use subtle inflections in the light curve to tease out the amount of time the planet is fully over the surface of the star. They can estimate the theoretical amount of limb darkening, and they can use modeling – as you're doing here – to bring it all together and test their assumptions against actual observations.


Experimenting with Transit Photometry

Now that we have a working simulator, we can use it to model possible behaviors of transits, permitting better analysis of real-life observations in the future. One approach would be to run a lot of possible cases and produce an "atlas" of expected exoplanet responses. Researchers could use this atlas to help them interpret actual light curves.

Starspots

Sunspots – called starspots on alien suns – are regions of reduced surface temperature caused by variations in the star's magnetic field. Starspots can darken the face of stars and do interesting things to light curves.

To see an example, edit the previous script so that an exoplanet roughly the same size as a starspot passes over several during its transit. Change the following constants as indicated:

EXO_RADIUS = 4

EXO_START_Y = 205

Here's the result:

Starspots in the transit path result in a "bumpy" light curve (by the author)

When the exoplanet occludes (covers) a starspot, the overall effect is to brighten the image, as two dark spots become one. This, in turn, causes short-lived "bumps" in the light curve.

Asteroid Belts

Asymmetrical light curves may also be produced by asteroid fields. These belts of debris often originate from planetary collisions or the creation of a solar system, like the Trojan asteroids in Jupiter's orbit.

Trojan asteroids and Jupiter (courtesy of NASA)

The following code creates random asteroids using object-oriented programming (OOP). If you want reproducible asteroid objects, make sure that the line random.seed(15) is uncommented. Changing the seed number (15) will change both the asteroid sizes and their distribution.

"""Simulate transit of asteroids and plot light curve."""
import random
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import cv2 as cv

IMG_HT, IMG_WIDTH = 400, 500
BLACK_IMG = cv.imread('limb_darkening.png', cv.IMREAD_GRAYSCALE)
NUM_ASTEROIDS = 15
NUM_LOOPS = 170

random.seed(15) # Uncomment to permit reproducible asteroids.

class Asteroid():
    """Draws a circle on an image that represents an asteroid."""    
    def __init__(self, number):
        self.radius = random.choice((1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3))
        self.x = random.randint(-30, 60)
        self.y = random.randint(220, 230)
        self.dx = 3  

    def move_asteroid(self, image):
        """Draw and move an asteroid object."""
        cv.circle(image, (self.x, self.y), self.radius, 0, -1)
        self.x += self.dx

def create_dashboard(root):
    asteroid_list = []

    for i in range(NUM_ASTEROIDS):
        asteroid_list.append(Asteroid(i))

    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 6))
    canvas = FigureCanvasTkAgg(fig, master=root)
    canvas_widget = canvas.get_tk_widget()
    canvas_widget.pack(side=tk.TOP, fill=tk.BOTH, expand=1)    
    intensity_samples = []

    for _ in range(NUM_LOOPS):
        temp_img = BLACK_IMG.copy()        
        for ast in asteroid_list:
            ast.move_asteroid(temp_img)
        intensity = temp_img.mean()
        intensity_samples.append(intensity)
        relative_brightness = calc_rel_brightness(intensity_samples)        
        update_image(ax1, temp_img)
        update_light_curve(ax2, relative_brightness, 'k')
        canvas.draw()
        root.update()

def calc_rel_brightness(intensity_samples):
    max_brightness = max(intensity_samples)
    return [intensity / max_brightness for intensity in intensity_samples]

def update_image(ax, img):
    ax.clear()
    ax.imshow(img, cmap='gray')
    ax.axis('off')

def update_light_curve(ax, data, color):
    ax.clear()
    ax.plot(data, color=color, linestyle='dashed', linewidth=2, 
            label='Relative Brightness')
    ax.legend(loc='upper center')
    ax.set_title('Relative Brightness vs. Time')

if __name__ == "__main__":
    root = tk.Tk()
    root.title("Exoplanet Transit Dashboard")
    create_dashboard(root)
    root.mainloop()

Here's the output:

A simulated transit by an asteroid belt (by the author)

Transiting asteroids produce irregular and asymmetrical light curves. Interior "ledges" represent individual asteroids moving onto and off the face of the star.


Simulating for Fun

Now, let's run some unusual experiments on both edge cases and "edgy" cases. We'll run these experiments without the sun image, so we can focus entirely on the feature being simulated. For brevity, I'll provide links to the code for each simulation rather than explicitly include it.

Modeling an Exoplanet with an Exomoon

What happens if an exoplanet has an orbiting moon? Let's take a look (you can find the code for this simulation in this GitHub repository):

An exoplanet with an orbiting moon (by the author & Dr. Eric Mortenson)
Light curve for exoplanet with orbiting moon (by the author & Dr. Eric Mortenson)

A moon orbiting in the same plane as an exoplanet's orbit and parallel to Earth's orbit will produce a little bump in the light curve every time it is eclipsed by the exoplanet. NASA may have already observed this happening. You can see a video of the event here.

The light curve for Kepler-1625 suggests the presence of an exoplanet with moon (NASA)

Notice the similarities between the light curve for Kepler-1625 in the previous figure and our moon simulation. Thanks to modeling, the Kepler-1625 result should come as no surprise.

Detecting Alien Megastructures

In 2015, citizen scientists working on data from the Kepler space telescope noticed something odd about Tabby's Star, located in the constellation Cygnus. The star's light curve, recorded in 2013, exhibited irregular changes in brightness that were far too large to be caused by a planet.

Tabby's Star light curve measured by the Kepler Space Observatory (by the author from Wikipedia)

Besides the dramatic drop in brightness, the light curve was asymmetrical and included weird bumps that aren't seen in typical planetary transits. Proposed explanations posited that the light curve was caused by the consumption of a planet by the star, the transit of a cloud of disintegrating comets, a large ringed planet trailed by swarms of asteroids, or an alien megastructure.

Scientists speculated that an artificial structure of this size would most likely represent an attempt by an alien civilization to collect energy from its sun. Both science literature and science fiction describe these staggeringly large solar panel projects. Examples include Dyson swarms, Dyson spheres, ringworlds, and Pokrovsky shells.

Pokrovsky shell system of rings designed to intercept a star's radiation (Wikimedia Commons)

To simulate a megastructure, we'll replace the circular exoplanet used in the program with other simple geometric shapes. We don't need to match the curve exactly; we just need to capture key features such as the asymmetry, the "bump" seen around February 28, and the (very) large drop in brightness.

Here's my attempt using two huge but asymmetrical solar panels:

Simulated megastructure for Tabby's Star (by the author)
Simulated megastructure light curve for Tabby's Star (by the author)

This curve is very similar to the one for Tabby's Star. Believe it or not, I produced it with my very first attempt! You can find the code here.

This was fun, but we now know that whatever is orbiting Tabby's Star allows some wavelengths of light to pass, so it can't be a solid object. Based on this behavior and the wavelengths it absorbs, scientists believe dust is responsible for the weird shape of the star's light curve. Other stars, however, like HD 139139 in the constellation Libra, have bizarre light curves that remain unexplained.

Detecting an Alien Armada

Since we're already having fun, let's not hold back.

The hyper-evolved beavers of exoplanet BR549 have been as busy as, well, beavers. They've amassed an armada of colossal colony ships that are now loaded and ready to leave orbit. Thanks to some exoplanet detection of their own, they've decided to abandon their chewed-out home world for the lush green forests of Earth!

Would we be able to detect this armada using a light curve? Let's find out. You can find the code for this simulation here.

An alien armada transiting an alien star (by the author)
Light curve for the alien armada simulation (by the author)

The orbiting spaceships produced an asymmetrical and irregular light curve. Based on the example of Tabby's Star, this would surely garner interest, but I'll bet no astronomer (except maybe Avi Loeb) would have the courage to suggest its true origin! At any rate, you wouldn't want to reach this conclusion until after you've run exhaustive simulations with asteroids, multiple exoplanets, comet swarms, dust clouds, and other natural phenomena.


Summary

I hope you enjoyed this small project and gained an appreciation for computer simulations. As versatile and powerful tools for scientific research, they can help scientists and engineers understand complex phenomena and design efficient experiments. Their advantages include:

  • The ability to model intricate systems that may be challenging or impossible to study directly. This includes avoiding safety hazards associated with field studies.
  • The design of efficient and cost-effective experiments that are not possible in the real world. Just as we didn't need an observatory to simulate exoplanet transits, simulations can eliminate the need for expensive equipment and resources and shorten the time it takes to complete a study.
  • The use of sensitivity analyses to predict and address the impacts of changing parameters. This helps identify critical factors and eliminate the need for expensive trials-by-error.
  • The discovery of emergent phenomena that may not be immediately apparent in real-world observations. This may lead to other discoveries not even dreamed of before running the simulation.
  • The optimization of designs and processes for the most efficient or effective solutions.
  • The generation of compelling and engaging educational tools for students in a classroom or managers in a boardroom. If a picture is worth a thousand words, a good simulation is worth a million.
  • Contributions to predictive modeling, enabling researchers to forecast future trends, behaviors, or events. This is especially crucial in fields like economics, climatology, and epidemiology.

While simulations can't completely replace real-world experiments, they provide numerous advantages in cost, time, safety, and the ability to explore and understand complex systems.


Thanks

Thanks for reading and please follow me for more Quick Success Data Science projects in the future. And for more on discovering exoplanets, check out Chapter 8 of my book, Real-world Python.

Real-World Python: A Hacker's Guide to Solving Problems with Code

Tags: Computer Simulation Editors Pick Exoplanets Opencv Python Python Programming

Comment