Last edited 11dec11 by axiao2@illinois.edu
Find this document at http://new.math.uiuc.edu/math198/

Orrery 2011

Albert Xiao

Abstract

My project is the creation of an orrery, which is a model of the Solar System. The orrery is programmed using the PyOpenGL graphics library. The program is three dimensional, and the user is able to navigate the Solar System through all three dimensions by using keys to move through space and rotate the camera. The orrery does not use programmed orbits, but instead uses Newtonian gravity to calculate the movements of the planets at any given time. The orrery is also highly manipulable, allowing the user to alter planet sizes, orbital distances from the sun, the time scale, and trail settings.

Program Usage

Key(s)Function(s) (If it's a "change," use shift + key to get the opposite change)
(z)apReset initial conditions
(s)ide viewPuts user in side view
(f)ront viewPuts user in front view
time (c)hangeChanges time scale linearly
time (j)umpChanges time scale exponentially (caution!)
trail (o)n/offToggles trail visibility
trail (l)engthChanges trail length
scale mo(d)elChanges between a scale model and a visible model
(esc)apeExits the program
(0-8)Changes sizes of individual bodies
(q,w,e,r,t,y,u,i)Changes radii of orbits without changing physics
up/downMove forward and backward along line of sight
left/rightSwivel head left and right
shift + up/downTilt head up and down

Table 1. List of usable keys and their uses.

Navigation

Figure 1. View of the orrery at the start of the program, with planets somewhat enlarged. GIF made by putting together 10 images using Make a GIF.

At the start of the program, the planets look similar to Figure 1, although the planets have been in Figure 1 varyingly enlarged using key functions which will be explained later. Due to the radii of the planets and the radii of orbits of the planets, in order to have a view of the Solar System that encompasses all of the planets, the viewer needs to be so far away from the Sun that the Solar System itself is impossible to see. This can be verified by pressing the (d) key, which changes between a scale-model orrery, which uses the true radii of the bodies, and a not-to-scale-model orrery, which uses radii that I chose in order to make the orrery visible without completely destroying the relative sizes of the bodies. The reason why you can see the bodies in this view is the program doesn't use the true radii of the planets, initially.

In order to navigate the orrery, the arrow keys and the shift key are used. Their uses are described in Table 1. In Figure 2, I have used the up arrow key to move closer to the Sun. As you can see, the inner planets are much easier to see from a closer distance. More complicated moves require combinations of the keys. For example, if you want to move to the left, you have to swivel your head to the left, using the left arrow key, and then move forward, using the up arrow key, into the left direction.

Figure 2. A zoomed-in view of the inner planets, with trails.

Navigating the Solar system can be somewhat confusing at first. Don't worry if you get lost; you can return to the initial view using the (f) key or the (z) key. The (f) key merely changes the view, whereas the (z) key resets everything except the planets' positions and velocities and the year. You can also use the (s) key to move to a side view of the Solar System, seen in Figure 3.

Figure 3. A side view of the planets. Of interest is how far off the plane of orbit Neptune is, compared to the other planets.

This program uses the gluPerspective and gluLookAt functions, and the navigation system uses the gluLookAt command to change the position of the user and the direction that the user is looking at. The navigation used in this program is adapted from the nbody.py in Stan Blank's "Python Programming in OpenGL."[1]

Trails

As you've probably noticed, the planets in Figures 2 and 3 have trails. Trails can be turned on and off using the (o) key. The trails are created using GL_LINE_STRIP. The colors of the trail are the same as the colors of the planets that have the trails, and the vertices of the trails are old positions of the planets, which are stored in a list. 50 verticies are plotted per trail. The lengths of the trails are adjustable in two ways: the (l) key changes the number of times the position of the planet updates between the vertices used in the line strip. The (c) and (j) keys change the time step, which will result in a the planets moving different distances per update, which will in turn change the length of the trails. Since the trails are determined by positions of planets over different time intervals, it comes as no surprise that the lengths of the trails are proportional to the velocities of the planets.

Radii Manipulation

Figure 4. The orrery has been manipulated so that the planets are roughly equal in size and their orbits have been arranged neatly.

Due to the vast differences between the sizes of the planets and their orbital distances, I decided that having the ability to adjust the radii of the bodies and their radii of orbits would be convenient. The radii of the bodies can be changed using the number keys, where (0) is the sun, and (1-8) are the planets. The radii of orbits can be changed using the top row of letters on a QWERTY keyboard, where (q) is Mercury and (i) is Neptune. These changes are purely visual, and do not affect the physics calculations in any way. The radii of the planets are stored in an array. In another array, multipliers to these radii are stored. The number keys adjust these multipliers, and the radii that are displayed are the result of multiplying these two arrays. I implemented the changing of the radii of orbit in a similar manner. The positions of the planets are stored in an array. In another array, multipliers are stored. These multipliers are changed using the top row of a QWERTY keyboard. Since the center of my orrery is (0,0,0), multiplying these two arrays will result in a scaled change of the radii of orbits of the planets. The distances of the planets from the sun are displayed as the product of these two arrays, but for the purpose of the physics calculations, only the first array is used. As a result, increasing the radius of orbit of a planet using this method will make it appear to move faster, since its orbital period remains the same, but it appears to move over a larger distance.

Time Manipulation

Figure 5. The result of increasing the time step too much. As you can tell by the planet trails, Mercury, Venus, and Earth have flown out of the Solar System

I've said that the time step can be changed linearly with (c) or exponentially with (j). What does this mean? As I'll discuss in the next section, the time step is used for calculation of the physics. The time step is the amount of time that elapses in my orrery per update. As such, increasing the time step will result in the planets move over larger distances per update, which appears to us as if the planets are moving faster. Adjusting the time step allows us to do interesting things: we can make the planets go extremely fast, we can make them stop moving, and we can make them go backwards through time. The year that the orrery is in is displayed by the system. The starting date is November 17, 1992. Be careful when adjusting the time step though, because if the time step is too large, things like Figure 5 can happen. Why they happen will be discussed in the next section.

Other Implementation Details

Figure 6. Equations from classical mechanics relevant to the implementation of the physics in this orrery.

All the planet data used to create this program were found at the NASA Jet Propulsion Laboratory website[2] and are stored in Numpy arrays. The planets start with the positions and velocities they had on November 17, 1992.

This orrery is a specific case of the N-Body problem, and as such, I've used N-Body problem Python codes as skeletons for my program. The way the arrays are used to calculate forces, momenta, and positions are taken from stars.py in the VPython examples library. The structure of the code and some of the properties of my navigation system were taken from Stan Blank's "Python Programming in OpenGL."[1]

As we can see right away, the sun is unique among the bodies. This is because the sun has been made to emit light, using OpenGL lighting, whereas the planets reflect the light.

The program starts by setting up the planet data, physics data, and the user's view. Planets' masses, velocities, positions, colors, and radii are all stored into arrays. Although the true radii are stored in the program, they are not used initially, for reasons previously stated. The universal gravitational constant, 6.7e-11, and a time step, 10000 seconds, are also set up in the beginning. These are used in the calculation of the trajectories of the bodies. A list is created that stores the bodies' past positions, used to create trails. And finally, the user's view is set up using the gluLookAt and gluPerspective functions.

The motions of the planets in this orrery are calculated using Newtonian gravity,[3] and the relevant equations are shown in Figure 6. Newton conceived force as the derivative of momentum with respect to time, so that is how I am using force in my program. For each planet, the sum of the force vectors acting on that planet by every other planet is given by the universal gravitational constant times the sum of the mass of that planet times the mass of the other planets divided by the distance between that planet and the other planets squared times the unit direction vector between that planet and the other planets. Once you have the gravitational force, you can calculate the change in momentum of the planet, the change in velocity of the planet, and the change in position of the planet, following the equations in Figure 6 in order. The time step I referred to previously is dt in these equations. Ideally, I would be able to make dt approach zero and integrate these equations to obtain a closed-form equation that describes the paths of all the planets. Unfortunately, the N-Body problem, and thus this problem, can't be reduced to a closed-form equation. Thus, for the purposes of my program, I am using leapfrog integration[4]. Leapfrog integration is an approximation of an integral that is used for a non-zero dt. Using leapfrog integration in my program, at each update, the program updates the position of the planets using the previously calculated velocities using half of the dt, then it updates the velocities based on the gravitational forces at work, and then it updates the position of the planets using the newly calculated velocities using the new dt. This is conceptually similar to the middle Riemann sum. Although this form of integration is not perfect, as long as the dt used in the program is not too high, there will be no noticeable problems. As dt increases, the accuracy of my program with respect to the actual Solar System decreases. Eventually, stuff like Figure 5 happens.

Bibliography

  • [1] Blank, Stan. Python Programming in OpenGL: A Graphical Approach to Programming. Department of Mathematics - University of Illinois at Urbana-Champaign. 8 Apr. 2008. Web. 11 Dec. 2011. <http://www.math.uiuc.edu/~gfrancis/illimath/StanBlank/PyOpenGL.pdf>.

  • [2] "HORIZONS Web-Interface." JPL Solar System Dynamics. Web. 11 Dec. 2011. <http://ssd.jpl.nasa.gov/horizons.cgi>.

  • [3] "Newton's Law of Universal Gravitation." Wikipedia, the Free Encyclopedia. Web. 11 Dec. 2011. <http://en.wikipedia.org/wiki/Newton%27s_law_of_universal_gravitation>.

  • [4] "Leapfrog Integration." Wikipedia, the Free Encyclopedia. Web. 11 Dec. 2011. <http://en.wikipedia.org/wiki/Leapfrog_integration>.