Note
Go to the end to download the full example code.
Temporary Earth Capture
Some near-Earth asteroids pass close enough to Earth that they become temporarily captured, orbiting the planet for weeks or months before escaping back onto heliocentric orbits. These so-called “mini-moons” spend time inside Earth’s Hill sphere and can have geocentric specific energy that dips below zero during capture.
This example uses 2020 CD3 – the second known natural object to be temporarily captured by Earth – to demonstrate kete’s orbital analysis tools: geocentric specific energy and Earth’s Hill sphere / sphere of influence.
import matplotlib.pyplot as plt
import numpy as np
import kete
Fetch 2020 CD3 and set up the time window
We fetch the orbit from JPL Horizons and define a window covering the temporary capture event (~Aug 2020 to ~Mar 2021).
obj = kete.HorizonsProperties.fetch("2020 CD3")
jd_start = kete.Time.from_ymd(2010, 1, 1).jd
jd_end = kete.Time.from_ymd(2040, 1, 1).jd
# Propagate to the start of the window
state = kete.propagate_n_body(obj.state, jd_start)
Physical constants
Earth’s Hill sphere and sphere of influence.
planet = "Earth"
earth_hill = kete.hill_radius(planet)
earth_soi = kete.sphere_of_influence(planet)
print(f"Earth Hill radius: {earth_hill * kete.constants.AU_KM:.0f} km")
print(f"Earth sphere of influence: {earth_soi * kete.constants.AU_KM:.0f} km")
Earth Hill radius: 1471600 km
Earth sphere of influence: 925065 km
Propagate through the capture event
Step through the time window recording geocentric distance and specific energy at each epoch.
step = 7.0 # days
times = np.arange(jd_start, jd_end, step)
geo_dist_km = []
spec_energy = []
for jd in times:
state = kete.propagate_n_body(state, jd)
# Geocentric state for distance and specific energy
geo_state = state.change_center(planet)
r_km = geo_state.pos.r * kete.constants.AU_KM
geo_dist_km.append(r_km)
energy = kete.specific_energy(geo_state)
spec_energy.append(energy)
geo_dist_km = np.array(geo_dist_km)
spec_energy = np.array(spec_energy)
# Convert JD to fractional year for the x-axis
t_years = [kete.Time(jd).year_float for jd in times]
Plot the results
Two panels show the capture event:
Geocentric distance relative to Earth’s Hill sphere and sphere of influence.
Geocentric specific energy – negative values indicate a bound orbit around Earth.
fig, axes = plt.subplots(2, 1, figsize=(9, 6), sharex=True, dpi=150)
ax = axes[0]
ax.plot(t_years, geo_dist_km, "k-", lw=0.5)
ax.axhline(
earth_hill * kete.constants.AU_KM,
color="blue",
ls="--",
lw=0.8,
label=f"Hill sphere ({earth_hill * kete.constants.AU_KM:.0f} km)",
)
ax.axhline(
earth_soi * kete.constants.AU_KM,
color="green",
ls=":",
lw=0.8,
label=f"SOI ({earth_soi * kete.constants.AU_KM:.0f} km)",
)
ax.set_ylabel("Geocentric distance (km)")
ax.set_yscale("log")
ax.legend(fontsize=8)
ax.set_title(f"{obj.desig} -- Temporary Earth Capture")
ax = axes[1]
ax.plot(t_years, spec_energy, "k-", lw=0.5)
ax.axhline(0, color="red", ls="--", lw=0.8, label="Bound / unbound boundary")
ax.set_ylabel("Specific energy (AU$^2$/day$^2$)")
ax.set_xlabel("Year")
thresh = 10 ** (np.ceil(np.log10(np.abs(np.min(spec_energy)))))
ax.set_yscale("symlog", linthresh=thresh)
ax.legend(fontsize=8)
plt.tight_layout()
plt.show()

Total running time of the script: (0 minutes 9.311 seconds)