exoatlas provides some basic tools for helping identify targets that might be observable, either for quick single visits or for extended transit observations that need to be scheduled at very specific times. To demonstrate, letβs make a few populations of exoplanets.
import exoatlas as ea
import astropy.units as u
import matplotlib.pyplot as plt
ea.version()'0.7.9'e = ea.TransitingExoplanets()nearby_transiting_planets = e[e.distance() < 20 * u.pc]
one_transiting_planet = e["GJ1214b"]e, nearby_transiting_planets, one_transiting_planet(β¨ Transiting Exoplanets | 4554 elements β¨,
β¨ Transiting Exoplanets | 52 elements β¨,
β¨ GJ1214b | 1 elements β¨)Whatβs up at night?ΒΆ
Letβs imagine we want to point a telescope at an exoplanet system to take an image or spectrum of it. At a particular moment on a particular night from a particular observatory location, weβll need to know if the star is visible in our local sky. To start, letβs define the observatory location and time at which we want to observe:
from astroplan import Observer
observatory = Observer.at_site("APO", timezone="US/Mountain")
observatory<Observer: name='APO',
location (lon, lat, el)=(-105.82000000000002 deg, 32.78000000000001 deg, 2798.0000000005284 m),
timezone=<DstTzInfo 'US/Mountain' LMT-1 day, 17:00:00 STD>>from astropy.time import Time
date = Time("2025-06-01")
night = observatory.midnight(date)Now, we can calculate the altiude and azimuth of all elements from that observatory at that time. We can also calculate the βairmassβ ( = how much more Earth atmosphere we look through compared to zenith), where targets with airmass > 2 start might start getting a little too low toward the horizon to be worth observing.
positions = e.altaz(where=observatory, when=night)
airmass = e.airmass(where=observatory, when=night)fi, ax = plt.subplots(2, 1, constrained_layout=True, figsize=(8, 8))
plt.sca(ax[0])
kw = dict(c=airmass, cmap="Reds_r", vmax=2, marker=".")
plt.title(f"What's up? | {night.iso} | {observatory.name}")
plt.scatter(e.ra(), e.dec(), **kw)
plt.xlabel("Right Ascension (degrees)")
plt.ylabel("Declination (degrees)")
plt.xlim(360, 0)
plt.ylim(-90, 90)
plt.colorbar(label='airmass')
plt.sca(ax[1])
plt.scatter(positions.az, positions.alt, **kw)
plt.xlabel("Azimuth (degrees)")
plt.ylabel("Altitude (degrees)")
plt.xlim(360, 0)
plt.ylim(-90, 90)
plt.colorbar(label='airmass');
Neat! It looks like the Kepler field (= the very dense blob) is high in the sky this time of year!
When can we observe a transit?ΒΆ
A slightly tricker question is when another transit can be observed for a transiting exoplanet system. For observing a transit, we need the star to be up, the Sun to be down, and the planet to pass in front of the star. We can calculate when all these stars align using astroplan, wrapped inside the .show_upcoming_transits method.
We can find observable transits for a single planet by specifying:
whereas anastroplan.Observerobject for the location from which youβre observingwhenas anastropy.time.Timeobject for the time from which youβd like to start considering transitswindowas anastropy.unit.Quantityobject with units of time, to indicate how long of a window over which youβd like to search for transits
a_few_transits = one_transiting_planet.show_upcoming_transits(
where=observatory, when=date, window=10 * u.day
)---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[9], line 1
----> 1 a_few_transits = one_transiting_planet.show_upcoming_transits(
2 where=observatory, when=date, window=10 * u.day
3 )
File ~/Dropbox/zach/code/exoatlas/exoatlas/calculations/planning.py:306, in show_upcoming_transits(self, where, when, window, allow_partial_transits, min_altitude, max_altitude, orbital_phase, visualize)
304 if visualize:
305 for row in transit_planning_table:
--> 306 plot_airmass_for_transit(row, savefig=True)
308 return transit_planning_table
File ~/Dropbox/zach/code/exoatlas/exoatlas/calculations/planning.py:96, in plot_airmass_for_transit(row, max_airmass, savefig)
93 plt.figure()
95 # use astroplan to do most of the work
---> 96 plot_airmass(
97 targets=row["target"],
98 observer=row.meta["where"],
99 time=row["midpoint"],
100 brightness_shading=True,
101 altitude_yaxis=True,
102 max_airmass=max_airmass,
103 style_kwargs=dict(color="black"),
104 )
106 # plot the transit as vertical lines
107 plt.axvline(
108 row["midpoint"].plot_date,
109 color="black",
110 linestyle="--",
111 label="mid-transit",
112 )
File ~/miniconda3/envs/exoatlas/lib/python3.14/site-packages/astroplan/plots/time_dependent.py:196, in plot_airmass(targets, observer, time, ax, style_kwargs, style_sheet, brightness_shading, altitude_yaxis, min_airmass, min_region, max_airmass, max_region, use_local_tz)
193 target_name = ''
195 # Plot data (against timezone-offset time)
--> 196 ax.plot_date(timetoplot.plot_date, masked_airmass, label=target_name, **style_kwargs)
198 # Format the time axis
199 xlo, xhi = (timetoplot[0]), (timetoplot[-1])
AttributeError: 'Axes' object has no attribute 'plot_date'
This function returns a table, indicating the transit ingress, midoint, and egress, all as astropy Time objects.
a_few_transitsWe can also search for any observable transits for any planet in a larger population.
lots_of_transits = nearby_transiting_planets.show_upcoming_transits(
where=observatory, when=date, window=10 * u.day
)If any planets in a population are missing the data needed to plan a transit (period, transit_midpoint, transit_duration), theyβll appear in a table indicating what needs to fixed (for example, with .update_values) in order for transits to be predicted.