Personal

Solar System Simulation

  • Python
  • NumPy
  • Matplotlib
  • PyGame
Orbit visualisation

01

Overview

I built this project during my A-levels. It is designed to be used as a learning tool for A-level and GCSE students. Astrophysics is very difficult for students to run their own experiments on (for obvious reasons). This leads to students struggling to visualise key concepts and poor performance on exam questions. The process of building this project improved my own understanding of physics, leading to my interest in engineering and simulation software.

02

The Physics

The acceleration of each planet is calculated at each frame. This is added to its velocity vector, which is then used to update its position.

F=GMmr2a=dvdtv=dxdtF = \dfrac{GMm}{r^{2}} \qquad a = \dfrac{dv}{dt} \qquad v = \dfrac{dx}{dt}

Users have the freedom to modify the radius and density of planets to see how this affects the gravitational field of the space. I chose not to model the gravitational force between planets in order to keep their orbits stable (unless they choose to modify the mass of the sun). In order to visualise the gravitational field of the whole system, users can add meteors which follow the resultant gravitational field.

Jupiter's mass increased ×10,000 — every meteor's acceleration line swings around to point straight at it.

03

The Graphics

Building the 3D graphics engine

PyGame only supports 2D shape drawing, so I studied how 3D graphics actually work so that I could display them on a 2D screen. I used linear algebra techniques to implement: back-face culling, face shading and depth perception. This was fairly simple to apply because all the planets are convex shapes.

calcUnitVector — guards the Sun-at-origin case

def ...
Back-face culling treats the ring as a set of ordinary planet faces, and gets the near/far edges wrong.
Fixed by splitting the ring into line segments and depth-sorting each one against the planet centre.

Getting the scale right

The solar system is difficult to visualise on a linear scale, so I scaled radius and orbital distance by independent logarithmic sclaes. This improves the user experience by having all planets visible simultaneously.

30 user-created planets — still comfortably interactive.
A stress test few students would ever reach: 500 meteors fired in one sitting.

04

Validation & Testing

Validating against NASA data

To check the physics was actually right and not just plausible-looking, I seeded the simulation with Mercury's real position, velocity and mass from NASA's planetary fact sheet, ran it for three orbits, and compared the measured orbital period and mean distance against the published values. My threshold for a pass was within 5%.

96.6%
orbital period accuracy

0.233 yr measured vs. 0.241 yr actual, averaged over 3 orbits of Mercury

97.9%
orbital distance accuracy

56.66M km measured mean distance vs. 57.9M km actual

±0.8%
spread from float rounding

measured by seeding many Mercury-like orbits at random starting angles

169 orbits
vs. 168.37 expected

after 168.37 virtual years — about 30 real minutes of runtime

The remaining error traces to three places: the initial conditions use averaged rather than instantaneous position and velocity, acceleration is only recalculated 100 times a second rather than continuously, and 64-bit floats round differently depending on a planet's starting angle. I isolated that last one by seeding dozens of Mercury-like orbits at random angles around the Sun and plotting them — if the physics were exact they'd land on exactly the same point on a T2T^2 vs. R3R^3 graph; the small scatter that appears instead is rounding error, not a modelling mistake.

Kepler's third law says T2∝R3T^2 \propto R^3 for every planet orbiting the same star, with the constant of proportionality GM⊙4π2\dfrac{GM_\odot}{4\pi^2}. Plotting the simulation's own output for all eight planets is the real end-to-end test: it doesn't just check one planet against one fact sheet, it checks whether the whole system's dynamics are self-consistent. On a linear scale the four inner planets collapse into an unreadable cluster in the corner, so I plotted it again on a log-log scale.

Linear scale — Mercury, Venus, Earth and Mars are indistinguishable at this range.
Log-log scale — every planet the simulation produced, sitting on Kepler's line.

The graph above only proves Kepler's law — it doesn't let a student ask their own question of the data. So every run also writes each planet's live position, velocity and orbital data out to an Excel spreadsheet via Openpyxl, overwriting the previous run rather than leaving a folder of near-duplicate files behind. A student who wants to test something the built-in graph plotter doesn't cover — checking F=maF=ma by hand, say — can just open the numbers themselves.

Every run exports its own data to OrbitData.xlsx — the raw numbers behind the graph above, for a student to check by hand.