Rodney Dunning's Home Page | Research and Scholarship | VPython | Simultaneity


Introduction

Suppose two firecrackers at different locations explode at the same time, with their positions and the time of the explosions determined by clocks and meter sticks which are at rest relative to you. An observer moving relative to you will claim the firecrackers did not explode simultaneously, i.e., one exploded before the other. Physically, this claim is just as correct as your observation.

Einstein's special theory of relativity predicts that events which are simultaneous in one reference frame will not be simultaneous in another reference frame, unless the events occur at the same location.

This animation was inspired by Figures 3-2 and 3-3 in Lewis C. Epstein, Relativity Visualized, (Insight Press, San Francisco, 1985), pp. 35-36.

Description

The animation shows three rockets. The flag ship, in the center, is piloted by and labeled "Alice." The lead ship is labeled "Bob," and the rear ship is labeled "Claire." Each rocket has a flag that will raise at a predetermined event.

Alice's rocket sends a light signal that reaches both Claire and Bob. The signal appears as a ring. When the signal reaches Claire and Bob, as measured in Alice's reference frame, Alice raises her flag. Claire and Bob each raise their flag when the signal from Alice is received.

A sequence of five events takes place:

If the sequence of events is viewed with the rockets at rest, events B, C, and D occur simultaneously. If the sequence of events is viewed with the rockets moving to the right (with Bob in the lead), event C occurs before event D, which occurs before event B.

When you execute the program, you are prompted for the speed of the Other Frame, attached to the rockets, relative to the Home Frame, attached to the animation window.

Screen Shots

The rockets are viewed in a frame in which they are at rest
The rockets are viewed in a frame in which they are at rest. The light signal from Alice (the largest circle) reaches Claire and Bob at the same time.

The rockets are viewed in a frame in which they move to the right 
          at 50% of the speed of light.
The rockets are viewed in a frame in which they move to the right at 50% of the speed of light. The signal from Alice (the largest circle), reaches Claire before it reached Bob.

Suggested Use

What I did:

  1. First run the program with zero speed for the Other Frame. This allows students to view the events from within the Other Frame, which is attached to the ships. Ask the students to observe whether the flags are raised simultaneously.
  2. Run the program with the ships moving at 50% of the speed of light. (Enter 0.5 for the speed of the Other Frame.) Ask the students to observe whether the flags are raised simultaneously.
  3. Use the mouse coordinates and the clock to determine event coordinates in the Other Frame, attached to the rocket, and the Home Frame, attached to the animation window. Verify that the animation's results are consistent with the Lorentz transformation equations. I show some sample calculations below.

Hints:

Recall the Lorentz transformation equations:

t = γ(t' + βx')

x = γ(x' + βt')

The Other Frame is attached to the rockets. The Home Frame is attached to the animation window.  Suppose the Other Frame speed relative to the Home Frame is β = 0.5. Then γ = 1.1547.

Event A: Alice sends the signal. The Other Frame coordinates are t = 0, x = 0. The Home Frame coordinates should be t' = 0 and x' = 0.

Event B: Bob, in the lead ship, receives Alice's signal. The Other Frame coordinates are t' = 100 s, x' = 100 s. The Home Frame coordinates should be t = 173.2 s, x = 173.2 s.

Event C: Claire, in the rear ship, receives Alice's signal. The Other Frame coordinates are t' = 100 s, x' = -100 s. The Home Frame coordinates should be t = 57.7 s, x = 57.7 s.

You can use the animation to confirm that the Home Frame coordinates are correct for each of these events.

Source Code

The source code appears between the bars below. Copy and paste the code into the Python IDLE environment, and hit F5 to run the code.


from visual import *
from __future__ import division

"""

Rodney Dunning
Assistant Professor of Physics
Longwood University

See Figures 3-2 and 3-3 in Epstein's
"Relativity Visualized," pp. 35-36.


The Other Frame is attached to the ships.  The
Home Frame is attached to the animation window.

User enters the speed of the Other Frame
relative to his own frame, the Home Frame.

The ships are spaced 100 light-seconds
apart.  At t = 0.0 seconds in the Home
Frame and Other Frame, a signal is sent from the
central ship to the lead and rear ships.  Upon
reception of the signal, each ship sends a
signal back to the central ship.

"""

#----------------------------
# The clock class
#----------------------------

class clock:
    def __init__(self,
                 t = 0.0,
                 dt = 0.01,
                 t_max = 1000.0):

        self.t = t
        self.dt = dt
        self.t_max = t_max
        self.label = label(pos=(0,0,0),
                           text="Clock")

    def tick(self):
        self.t += self.dt

    def full_time(self):
        return self.t >= self.t_max

    def set_time(self,t):
        self.t = t

    def reset(self):
        self.t = 0

#----------------------------------
# The ship class
#----------------------------------

class ship:
    def __init__(self,
                 pos = vector(0,0,0), # position
                 vel = vector(0,0,0), # velocity
                 acc = vector(0,0,0), # acceleration
                 force = vector(0,0,0), # force
                 mass = 1.0, # common mass of the ships
                 L = 25.0, # common length of the ships
                 W = 10.0,  # common width of the ships
                 H = 5.0, # common height of the ships
                 fL = 25.0/7.0, #flag length
                 fW = 10.0/20.0, # flag width
                 fH = 5.0, #flag height
                 pR = 0.5, # common radius of the flag poles
                 theta = 0, # initial angle of the flag
                 d_theta = 0.01, #step size for rotating the flag
                 label_text = "Ship"):
                                  
        self.Lproper = L

        self.ship = frame(pos=pos,
                          vel=vel,
                          acc=acc,
                          mass=mass,
                          force=force)

        self.body = box(frame=self.ship,
                        length = L,
                        width = W,
                        height = H)
        
        self.flag = frame(frame=self.ship,
                          angle=0,
                          step=d_theta)

        self.pole = cylinder(frame=self.flag,
                             axis=(L/1.25,0,0),
                             radius = pR,
                             color = color.green)

        self.cloth = box(frame=self.flag,
                         height = fH,
                         width = fW,
                         length = fL,
                         pos = (L/1.25 - H/2, H/2,0), #within the frame!
                         color=color.red)

        self.label = label(frame = self.ship,
                           pos = vector(0,-1.5*H,0), #within the frame!
                           box = 0,
                           text = label_text)

    def move(self,dt): # moves the ship foward one step
        self.ship.pos += self.ship.vel * dt

    def rotate_flag(self):
        pi = acos(-1.0) #3.14159....
        self.flag.rotate(angle=self.flag.step,
                         axis=(0,0,1))
        self.flag.angle += self.flag.step

#----------------------------------------
# The light signal class.
#----------------------------------------

class light_signal:
    def __init__(self,
                 pos = vector(0,0,0), # initial position
                 thickness = 0.5, # thickness of the ring
                 radius = 0, # initial radius
                 rate = 1.0 # speed of light
                 ):

        self.body = ring(pos=pos, # initial position
                         thickness = thickness,
                         axis=(0,0,1),
                         radius = radius,
                         rate = rate,
                         color = color.white)

    def propagate(self,dt):
        self.body.radius += self.body.rate * dt

#-------------------------
# The labels class
#-------------------------


class labels:
    def __init__(self,
                 list = [] #empty list of labels
                 ):

        self.list = list

    def toggle(self):
        for x in self.list:
            x.visible = abs(1 - x.visible)

#----------------------------
# Useful quantities
#
# SR units throughout
#----------------------------

ship_separation = vector(100,0,0)

flag_delay = mag(ship_separation)

c = 1 # speed of light

signal_time = 0.0 # signal time; part of the origin event
                  # in the Home and Other Frames.

#--------------------------------------------
# The user enters the speed of the Home Frame
# relative to his own frame.
#--------------------------------------------

user_finished = 0 # set to 1 when finished

while(not user_finished):
    beta = input("Enter the Other Frame speed relative to you: ")
    print " " # spacer
    if(0 <= beta and beta < 1):
        user_finished = 1
        gamma = 1 / sqrt(1 - beta**2)
        ship_separation /= gamma
        signal_time*=gamma
    else:
        print " " #spacer
        print "Input error.  Please enter a value between 0 and 1."


print " " # spacer
print "beta = ",beta
print "gamma = ",gamma
print "ship separation is ",ship_separation.x
print " " # spacer
print "Thank you. Please find the animation window."

#----------------------------------------
# Specify the scene attributes
#----------------------------------------

scene.title = "The Relativity of Simultaneity"
scene.x = 0
scene.y = 0
scene.width = 800
scene.height = 600
scene.range = (200,200,200)
scene.autoscale = 0 ##0 means autoscaling is OFF
scene.userzoom = 1 ##0 means user cannot zoom
scene.userspin = 1 ##0 means user cannot spin
scene.lights = [vector(0,0,1)]
scene.ambient = 0.5
scene.label = label(visible=0,
                    pos=(0,0,0),
                    xoffset = 0,
                    yoffset = 0,
                    text = ("Click to begin.\n"))
scene.mouse_label = label(visible=1,
                          pos=(0,0,0),
                          text="Mouse position")

def return_mouse_pos(scene):
    scene.mouse_label.text = ("mouse\n x: %3.2f   y: %3.2f"
                              %(scene.mouse.pos.x, scene.mouse.pos.y))

def check_for_pause(scene): #checks for pause request
    if scene.mouse.clicked:
        scene.mouse.getclick()
        pause(scene)

def pause(scene): #pauses the animation
    while 1:
        return_mouse_pos(scene)
        if scene.mouse.clicked:
            scene.mouse.getclick()
            break

scene.mouse_label.pos = vector(beta*scene.range.x,100,0)


""" We shift the center of the scene to keep all the events
    in the animation window, but the origin event in both
    Home and Other Frame is the initial signal sent by
    the flag ship.
"""

if(beta > 0):
    scene.center = vector(beta*scene.range.x,0,0)

#----------------------------------------
# Create a clock.
#----------------------------------------

master_clock = clock()
master_clock.t = -5.0
master_clock.t_max = 1000.0
master_clock.label.pos.x = beta*scene.range.x
master_clock.label.pos.y = -100
master_clock.label.text = "Master Clock"

#--------------------------------------
# Create three ships: the flag ship,
# the rear ship, and the lead ship.
#--------------------------------------

flag_ship = ship(pos=vector(0,0,0),
                 vel=vector(beta*c,0,0),
                 acc=vector(0,0,0),
                 label_text="Alice")

rear_ship = ship(pos=flag_ship.ship.pos - ship_separation,
                 vel=vector(beta*c,0,0),
                 acc=vector(0,0,0),
                 label_text="Claire")

lead_ship = ship(pos=flag_ship.ship.pos + ship_separation,
                 vel=vector(beta*c,0,0),
                 acc=vector(0,0,0),
                 label_text="Bob")

flag_ship.body.length = flag_ship.Lproper / gamma
rear_ship.body.length = flag_ship.Lproper / gamma
lead_ship.body.length = flag_ship.Lproper / gamma

#-----------------------------------------------
# Create a list of labels to toggle on and off
#-----------------------------------------------

label_list = labels(list=(master_clock.label,
                          scene.label,
                          scene.mouse_label,
                          lead_ship.label,
                          flag_ship.label,
                          rear_ship.label))

#---------------------------------------
# Animate the scene
#---------------------------------------

ready = 0
label_list.toggle()
while(not ready):
    ready = scene.mouse.getclick()
    label_list.toggle()

center_signal = 0 # set to 1 when the signal is created
rear_signal = 0 # set to 1 when signal is created
lead_signal = 0 # set to 1 when signal is created

center_flag = 0 # set to 1 when center flag is raised
rear_flag = 0 # set to 1 when signal is received
lead_flag = 0 # set to 1 when signal is received


while(not master_clock.full_time()):
##    rate(1000)  # change this number to change the animation speed
    check_for_pause(scene)
    return_mouse_pos(scene)
    master_clock.tick()
    master_clock.label.text = "Clock t= %3.2f" %master_clock.t

    flag_ship.move(master_clock.dt)
    rear_ship.move(master_clock.dt)
    lead_ship.move(master_clock.dt)

    if(master_clock.t > signal_time and not center_signal): #create a light signal
        signal_from_center = light_signal(pos=flag_ship.ship.pos)
        center_signal = 1

    if(rear_flag and not rear_signal): #create a rear signal
        signal_from_rear = light_signal(pos=rear_ship.ship.pos)
        rear_signal = 1

    if(lead_flag and not lead_signal): #create a lead signal
        signal_from_lead = light_signal(pos=lead_ship.ship.pos)
        lead_signal = 1
    
    if(rear_signal):
        signal_from_rear.propagate(master_clock.dt)

    if(lead_signal):
        signal_from_lead.propagate(master_clock.dt)

    if(center_signal):
        signal_from_center.propagate(master_clock.dt)

        """
            The following block of code rotates the center ship
            flag when, the in the reference frame of the center
            ship, its signals reach the front and rear ships.
            (These events occur simultaneously in the center
            ship's reference frame.)

            The master clock keeps the time in the Home Frame,
            the frame attached to the animation window.
            The Other Frame is attached to the ships.

            These frames will coincide if you entered zero for
            the speed of the Home Frame.

        """

        if( (signal_time + flag_delay*gamma) < master_clock.t
            and flag_ship.flag.angle < pi/2):
            flag_ship.rotate_flag()
            if(not center_flag): center_flag = 1

        """
            In the following blocks of code, the flags on the rear
            and lead ships are rotated when the radius of the signal
            sent from the flag ship exceeds the rear-flag or lead-flag
            separation distance.

        """

        if(mag(rear_ship.ship.pos - signal_from_center.body.pos) <
           signal_from_center.body.radius
           and
           rear_ship.flag.angle < pi/2):

            if(not rear_flag): rear_flag = 1
            rear_ship.rotate_flag()

        if(mag(lead_ship.ship.pos - signal_from_center.body.pos) <
           signal_from_center.body.radius
           and
           lead_ship.flag.angle < pi/2):
            if(not lead_flag): lead_flag = 1
            lead_ship.rotate_flag()

scene.label.text = ("Finished.  Close the window and\n"
                    "reopen the file to view the\n"
                    "animation again.\n"
                    "" #spacer
                    "Copyright, 2006.  Rodney Dunning.")
label_list.toggle()