Oct 23

Lecture Overview

Today I show an example of object-orientated design. The problem is orbital mechanics: we work for NASA and are tasked to develop a program which can compute the trajectory of a space probe launched towards Jupiter or some other planet. (Such software already exists). Because of the n-body problem, there is no explicit formula for the trajectory. Therefore, we are required to use n-body simulation.

To simulate the trajectory of our probe, we pick a time unit (say 10 minutes) and compute the location and velocity of the probe in chunks of 10 minutes. That is to say, currently in our simulation the probe is located somewhere and we can compute the force on the probe due to gravity from the sun and planets. We then assume the force on the probe is constant for the next 10 minutes (this is not correct but is a good approximation) and use that force to calculate the position and velocity after 10 minutes. We then repeat this calculation to find where the probe is in another 10 minutes. We can of course change the time-unit to get a better approximation and even add in things like engine firings (they just add another force into our computation).

Design

During design, one of the first tasks is usually to list out the major pieces of data needed in the program. For us, it is

One way is to put each of the above quantities into a global variable. This is how the Apollo Guidance Computer worked. (If you are interested, I suggest you read this book, it is a fascinating account of the computer for Apollo.)

Using object orientated design, we instead create separate objects each with some properties. For us, the objects will be one object for each planet, one object for the probe, and one object for the solar system. The planet and probe objects will have properties for the location, velocity, and mass, and methods for computing gravitational fields. The solar system object will contain a property with a list of planets, have a method for updating the locations, and have a method for drawing on the screen.

Code

Today just the start of the code: the planet class. In addition to what I talked about last time, I am using an initialization method (see here). This is a method __init__ which gets called right after the object is created from the template.

import math

class Planet:
    "A class to represent a planet"

    sun_name = "Sol"

    def __init__(self, iname, irad, im):
        self.name = iname
        self.radius = irad
        self.mass = im

    def get_volume(self):
        return 4.0/3 * math.pi * self.radius**3

    def get_density(self):
        return self.mass / self.get_volume()

    def set_sun(self, newsun):
        self.sun_name = newsun

    def set_name(self, newname):
        self.name = newname

    def set_position(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    def gravitational_field(self, x, y, z):
        "Computes the gravitational field (as a vector) generated by this planet at the given location"

        # Compute the distance.
        dist = math.sqrt((self.x - x)**2 + (self.y - y)**2 + (self.z - z)**2)
        # Compute a unit vector (tuple of three elements) pointing from the point (x,y,z) towards the planet.
        unit_vector = ((self.x - x)/dist, (self.y - y)/dist, (self.z - z)/dist)
        # Compute the field amount.
        field = 6.67e-11 * self.mass / dist**2
        # Return the field vector by scaling the unit vector by the field strength.
        return (unit_vector[0]*field, unit_vector[1]*field, unit_vector[2]*field)


# This creates a new object with initial property (sun_name, "Sol") and a bunch of methods.
# It then calls the __init__ method with the supplied parameters, which will set more properties.
p = Planet("Mars", 3396, 6.4e23)

print(p.name)
print(p.radius)
print(p.sun_name)

# This will call the get_volume method, passing p as the first parameter (self)
print(p.get_volume())
print(p.get_density())

# This will call the set_name method, passing p as the first parameter (self) and the string "Ares"
# as the second parameter (newname).
p.set_name("Ares")
print(p.name)

# This sets the name property directly.
p.name = "Mars"
print(p.name)

# Temporarily put Mars at the origin
p.set_position(0, 0, 0)
# Print the gravitational field generated by Mars at some (x,y,z) position in space.
print(p.gravitational_field(1e5, 0, 5e4))

# Create another planet
p2 = Planet("Alpha Centauri Bb", 5.6e8, 1.8e30)
# Both of the following lines do the same thing
p2.sun_name = "Alpha Centauri"
p2.set_sun("Alpha Centauri")

Exercises