Historical Comparison: Le Verrier’s Observations vs Modern Simulation
This notebook compares the historical residuals reported by Le Verrier in 1846 with our modernized numerical simulation. It validates that the ‘signal’ Le Verrier saw—the systematic drift of Uranus—is reproduced by a modern gravitational model when Neptune is omitted.
We load the 1846 residuals extracted from Le Verrier’s memoirs. We use the project’s data-loading convention to locate the files.
Code
data_dir = find_bundled_data_dir()hist_df = pd.read_csv(data_dir /'leverrier_historical_observations.csv')# Convert dates to Julian Dates for the integrator# Note: Astropy Time requires a list or array of stringshist_df['jd'] = Time(hist_df['date'].tolist()).jdhist_df.head()
date
year
observer
residual_arcsec
type
jd
0
1690-12-23
1690
Flamsteed
67.1
ancient
2338676.5
1
1712-03-22
1712
Flamsteed
92.7
ancient
2346435.5
2
1715-03-10
1715
Flamsteed
110.0
ancient
2347518.5
3
1750-09-25
1750
Lemonnier
19.0
ancient
2360501.5
4
1750-10-14
1750
Lemonnier
12.4
ancient
2360520.5
2. Run Modern Simulation
We integrate the solar system without Neptune, using JPL state vectors from 1781-01-01 as our initial conditions. This ‘forward model’ allows us to see how Uranus would have moved according to Newton if Neptune didn’t exist.
Code
svs = pd.read_csv(data_dir /'outer_planet_state_vectors_1781_01_01.csv')state = { row.planet: StateVector(row.x, row.y, row.z, row.vx, row.vy, row.vz)for row in svs.itertuples(index=False)}# Modern no-Neptune residuals over the span we have proxy observations for# (1781–1846). Residual = observed − modeled, angle-wrapped to (−π, π].## NOTE: do NOT reuse the notebook's existing `start_jd = 2371628.5` — that is# the 13 Mar 1781 discovery-date row. The state vectors and the proxy# observations share the 1781-01-01 epoch (JD 2371557.5), which is the first# proxy row.obs = pd.read_csv(data_dir /"uranus_observations_1781_1846.csv")proxy_jd = obs["datetime_jd"].to_numpy(dtype=float)proxy_lon = obs["longitude_rad"].to_numpy(dtype=float)proxy_start_jd =float(proxy_jd[0]) # 2371557.5 = 1781-01-01model_lons = simulate_uranus_longitudes_no_neptune( proxy_jd, proxy_start_jd, state["jupiter"], state["saturn"], state["uranus"])wrapped = ((proxy_lon - model_lons + np.pi) % (2* np.pi)) - np.pisim_residuals_arcsec = np.degrees(wrapped) *3600.0proxy_years =1781.0+ (proxy_jd - proxy_start_jd) /365.25
3. Visualize the Signal
The plot below shows Le Verrier’s reduced residuals (points) alongside the residual of a modern no-Neptune simulation against JPL-derived longitudes over 1781–1846 (dashed line). The two series are measured against different reference orbits, so their signs and shapes are not directly comparable: Le Verrier’s residuals are quoted against Bouvard’s fitted tables, while the modern curve compares to an exact 1781 state propagated without Neptune. What they share is what matters — both anomalies grow to order 100–130 arcseconds by 1846, surging after the 1822 Uranus–Neptune conjunction, far beyond what refitting any Keplerian orbit can absorb. The ancient points push the same conflict back another ninety years.
Code
def _build_historical(hist_df, proxy_years, sim_residuals_arcsec, *, theme="light"):import matplotlib.pyplot as plt pal = palette(theme) fig, ax = plt.subplots(figsize=fig_size("wide")) ancient = hist_df[hist_df["type"] =="ancient"] modern = hist_df[hist_df["type"].isin(["modern_discovery", "illustrative"])] ax.plot( ancient["year"], ancient["residual_arcsec"], "o", label="Ancient observations (Le Verrier 1846)", color=pal.before, alpha=0.7, ) ax.plot( modern["year"], modern["residual_arcsec"], "s", label="Modern / illustrative trend", color=pal.after, ) ax.plot( proxy_years, sim_residuals_arcsec, "--", label="Observed − no-Neptune model (1781–1846)", color=pal.spine, lw=1.5, ) truth_line(ax, 0.0, axis="y", theme=theme) ax.set_title("Historical residuals vs. modern simulation") ax.set_xlabel("Year") ax.set_ylabel("Heliocentric longitude residual (arcseconds)") ax.legend()return figdisplay( dual_render( _build_historical, hist_df, proxy_years, sim_residuals_arcsec, alt="Le Verrier's historical Uranus residuals overlaid with a modern no-Neptune simulation", ))