Module 3: Calculus Revisited - Computationally


Formulating a Universal Law

Exercise 1: Download and execute Incline.java. This is a simulation of a bead falling along an inclined wire with no friction. You can set the incline angle and also the mass of the bead.

From these observations (only) what can you conclude about the relationship between:

Let us use a more refined tool that will allow more accurate measurements:

Exercise 2: Download InclineSimulatorExample.java, InclineSimulator.java, Function.java, and SimplePlotPanel.java. Then, compile and execute InclineSimulatorExample. What do you observe is the relationship between distance and time?

Exercise 3: Modify the code in InclineSimulatorExample.java to compute the derivative at t=1, 2 ... etc. For example, to estimate the derivative at t=2, you would

What do you conclude from estimating the derivative? Repeat the derivative estimation using 0.0001 instead of 0.01. Can you explain what you observe?

Let's next explore the idea of instantaneous velocity:

Exercise 4: Consider the function f(x) = 3x+5. Use 0.1 and compute the instantaneous rate-of-change at x=1, at x=2, and x=3. Then use 0.01 and compute the same. What do you conclude? What can you conclude about the instaneous rate-of-change of any linear function f(x) = ax+b?

About instantaneous rate-of-change:

Exercise 5: Consider the function f(x) = 3x2+5.

Let's take a closer look at instantaneous velocity in our incline-simulator:

Exercise 6: Execute the above code and estimate the slope (by hand and by code). Then modify the code to estimate the derivative of the velocity function at t=1, t=2, ..., t=10.

Acceleration:

Exercise 7: At this point we might try formulating a general "law" about motion:

Exercise 8: Puzzle: can you measure 30o without a protractor? That is, can you create an incline at an angle of 30o with a flat object right now without an angle-measuring device?


Formulating a Universal Law - More Examples

A simpler experiment:

Exercise 9: Execute the above code. You will also need BallDropSimulator.java.

A variation:

Exercise 10: Execute the above code. You will also need BallTossSimulator.java. Then, modify the the code in main to estimate the velocity curve. What can you conclude about your universal law thus far?

Exercise 11: Download and execute Projectile.java.


Going the Other Way - from Acceleration to Distance

Before looking further into our universal law, let us take a closer look at the relationship between acceleration, velocity, and distance
     => Our law somehow accounts for these quantities.

Reconstructing motion from acceleration:

  • Suppose we know that acceleration is constant, could we reconstruct motion (the distance function)?

  • This is what we know:
    • The derivative of the distance-function d(t) is: the velocity function v(t).
    • The derivative of the velocity-function v(t) is: the acceleration function a(t).

  • What we'd like to do: construct d(t) when we know (or are given) a(t).

  • For example, if a(t)=5 (constant acceleration), then what is d(t)?

  • Let us first write a simple program to reconstruct v(t) using the following reasoning:
    • We know that a(t) = (v(t+s) - v(t)) / s for some small value s (e.g., s=0.01).
    • Then, rearranging gives us v(t+s) = v(t) + s a(t).
    • Suppose we know v(0) = 0.
           => (Turns out, we have to know the initial value.)
    • Then, we could compute v(0+s) = v(0) + s * a(0).
    • Now that we know v(s)
           => We can compute v(s+s) = v(s) + s * a(s).
    • Then, compute v(3s) = v(2s) + s * a(2s).
    • Then, compute v(4s) = v(3s) + s * a(3s).
    • ... etc

Exercise 12: Suppose a(t)=4.9 (acceleration is a constant value of 4.9), and that initial velocity is zero.

  • Draw a graph of a(t) in the range [0,10].
  • Use s=0.1 and compute by hand v(0.1), v(0.2), v(0.3), v(0.4), v(0.5). Show your calculations.
  • Explain why the value for v(0.5) makes sense.
  • What is the connection between the calculations you did and the terms "line" and "slope"? What is the equation of the line in question?

Next, let's write a small program to compute v(t):

  • We'll use a(t)=4.9 as an example.

  • Here's the program (source file)
    public class IntegrateAccel {
    
        public static void main (String[] argv)
        {
            // a(t) = 4.9 (constant acceleration).
            double a = 4.9;
    
            // Initial velocity.
            double v = 0;
    
            // Our small "change" interval 
            double s = 0.1;
    
            // Start and end time.
            double t = 0;
            double endTime = 0.5;
    
            while (t < endTime) {
                System.out.println (">> t=" + t + " v=" + v);
                v = v + s * a;
                t = t + s;
            }
    
            System.out.println ("Final: t=" + t + "  v=" + v);
        }
    
    }
        

  • Note:
    • The key statement is:
                  // Update velocity:
                  v = v + s * a;
             
    • Here, the "new" velocity (at time t+s) is the "old" velocity (at time t) plus the interval length (s) times the slope (a).
    • A less efficient, but perhaps more understandable way of writing the same code would be:
              while (t < endTime) {
                  // Print current time and velocity:
                  System.out.println (">> t=" + t + " v=" + v);
      
                  // Compute what the new velocity should be:
                  double next_v = v + s * a;
                  // Compute what the new time value should be:
                  double next_t = t + s;
      
                  // Now change both v and t to reflect the new values.
                  v = next_v;
                  t = next_t;
              }
              

  • We will rewrite the code slightly so that the loop is in a method by itself: (source file)
    public class IntegrateAccel2 {
    
        static double a;
        static double v;
        static double s;
        static double t;
    
        public static void main (String[] argv)
        {
            // a(t) = 4.9 (constant acceleration).
            a = 4.9;
    
            // Initial velocity.
            v = 0;
    
            // Our small "change" interval 
            s = 0.1;
    
            // Start and end time.
            t = 0;
    
            // First example of using the method:
            double finalVelocity = computeFinalVelocity (0.5);
            System.out.println ("t=" + t + "  finalv=" + finalVelocity);
    
    
            // Second example:
            finalVelocity = computeFinalVelocity (0.7);
            System.out.println ("t=" + t + "  finalv=" + finalVelocity);
        }
        
    
        public static double computeFinalVelocity (double endTime)
        {
    
            while (t < endTime) {
                // Update velocity:
                v = v + s * a;
                // Update time:
                t = t + s;
            }
    
            return v;
        }
    
    }
        
    Note:
    • We have now pushed the loop into a method that can be called repeatedly.
    • The key variables are now declared global to the class so that they are accessible from the method.

  • Alternatively, we could have written this as: (source file)
    public class IntegrateAccel3 {
    
        public static void main (String[] argv)
        {
            // First example of using the method:
            double finalVelocity = computeFinalVelocity (4.9, 0, 0, 0.5, 0.1);
            System.out.println ("t=" + 0.5 + "  finalv=" + finalVelocity);
    
    
            // Second example:
            finalVelocity = computeFinalVelocity (4.9, 0, 0, 0.7, 0.1);
            System.out.println ("t=" + 0.7 + "  finalv=" + finalVelocity);
        }
        
    
        public static double computeFinalVelocity (double a, double initialVelocity, double initialTime, double endTime, double s)
        {
            double v = initialVelocity;
            double t = initialTime;
    
            while (t < endTime) {
                // Update velocity:
                v = v + s * a;
                // Update time:
                t = t + s;
            }
    
            return v;
        }
    
    }
        
    Note:
    • All variables are now passed in as parameters.
    • The initial values of variables v, t are now set inside the computeFinalVelocity() method.

Exercise 13: Which approach above is better and why?

Let's now write some code to produce a complete velocity function:

  • In the above examples, we computed v(t) for t=0.5 and t=0.7
         => These are only two values.

  • What we want is the complete v(t) function.

  • To do this, we can use a Function object and store a number of values in our desired range, for example: (source file)
    public class IntegrateAccel4 {
    
        public static void main (String[] argv)
        {
            // Make the object.
            Function velocityCurve = new Function ("velocity");
            
            for (double end=0.1; end<=10; end+=0.1) {
                // Compute final velocity at desired x-value:
                double finalVelocity = computeFinalVelocity (4.9, 0, 0, end, 0.1);
                // Put the (x,y) values into the object.
                velocityCurve.add (end, finalVelocity);
            }
    
            // Display.
            velocityCurve.show ();
        }
        
    
        public static double computeFinalVelocity (double a, double initialVelocity, double initialTime, double endTime, double s)
        {
            double v = initialVelocity;
            double t = initialTime;
    
            while (t < endTime) {
                // Update velocity:
                v = v + s * a;
                // Update time:
                t = t + s;
            }
    
            return v;
        }
    
    }
        

Exercise 14: Execute the above code and confirm your earlier hand calculations. Then, explore the following issues:

  • There is something wasteful about the way we are creating v(t). Can you see what it is? Can the code be modified to make it more efficient?
  • Modify the program to print only v(10) (Remove the display of the function).
  • What is the effect of changing the interval size? Suppose the interval size is 0.001? Suppose the interval size is 2.0?

Now that we've computed the velocity function v(t), we can try to compute the distance function d(t):

  • Recall that v(t) is the derivative of d(t).
         => Use the same approach

  • Assume we know v(t) (or have computed it before).
    • By the definition of v(t): v(t) = (d(t+s) - d(t)) / s for some small value s.
    • Then, rearranging, we get d(t+s) = d(t) + s v(t).
    • Assume we know the initial value d(0).
    • Then d(0+s) = d(0) + s v(0).
    • After which we compute d(s+s) = d(s) + s v(s).
    • After which we compute d(3s) = d(2s) + s v(2s) ... etc.

  • Thus, we could write a program for compute d(t) as follows: (source file)
    public class IntegrateVelocity {
    
        public static void main (String[] argv)
        {
            // Compute velocity function as before.
            Function velocityCurve = new Function ("velocity");
            for (double end=0.1; end<=10; end+=0.1) {
                double finalVelocity = computeFinalVelocity (4.9, 0, 0, end, 0.1);
                velocityCurve.add (end, finalVelocity);
            }
    
            // Compute distance at t=5:
            double finalDistance = computeFinalDistance (velocityCurve, 0, 0, 5, 0.01);
            System.out.println ("t=5  d=" + finalDistance);
    
            // Compute distance at t=10:
            finalDistance = computeFinalDistance (velocityCurve, 0, 0, 10, 0.01);
            System.out.println ("t=10  d=" + finalDistance);
        }
        
    
        public static double computeFinalVelocity (double a, double initialVelocity, double initialTime, double endTime, double s)
        {
            // ... same as before ...
        }
    
    
        // This is similar to how we computed velocity.
    
        public static double computeFinalDistance (Function velocity, double initialDistance, double initialTime, double endTime, double s)
        {
            double d = initialDistance;
            double t = initialTime;
    
            while (t < endTime) {
                // Update distance:
                double v = velocity.get (t);
                d = d + s * v;
                // Update time:
                t = t + s;
            }
    
            return d;
        }
    
    
    }
        

Exercise 15: Execute the above code and confirm the results by comparing it to the results from the InclineSimulatorExample.java example from earlier. How exactly did you confirm the results? Then, explore the following issues:

  • There is something wasteful about the way we are creating d(t). Can you see what it is? Can the code be modified to make it more efficient?
  • What is the effect of changing the interval size? Try interval sizes of 0.1 and 1.0.
  • Why does interval size matter more in this case?

Can we combine the velocity and distance computations?

  • Look at the code that computes distance:
            while (t < endTime) {
                // Update distance:
                double v = velocity.get (t);
                d = d + s * v;
                // Update time:
                t = t + s;
            }
         
    • Here, we have already computed the velocity function v(t).
    • We simply retrieve stored values each time we need it in the loop.

  • Instead, we can compute velocity values on-the-fly because the loops for velocity and distance are really the same: (source file)
    public class IntegrateVelocity2 {
    
        public static void main (String[] argv)
        {
            // No need to compute velocity function.
    
            // Compute distance at t=5:
            double finalDistance = computeFinalDistance (0, 0, 0, 5, 0.01);
            System.out.println ("t=5  d=" + finalDistance);
    
            // Compute distance at t=10:
            finalDistance = computeFinalDistance (0, 0, 0, 10, 0.01);
            System.out.println ("t=10  d=" + finalDistance);
        }
        
    
        public static double computeFinalDistance (double  initialVelocity, double initialDistance, double initialTime, double endTime, double s)
        {
            // Constant acceleration.
            double a = 4.9;               
    
            // Set initial values.
            double v = initialVelocity;    
            double d = initialDistance;
            double t = initialTime;
    
            while (t < endTime) {
                // First compute velocity:
                v = v + s * a;
                // Then, update distance:
                d = d + s * v;
                // Update time:
                t = t + s;
            }
    
            return d;
        }
    
    }
         

  • Let's consider one more issue:
    • Should we change distance after (as above) or before changing velocity?
    • That is, what if we had written: (source file)
      public class IntegrateVelocity3 {
      
          public static void main (String[] argv)
          {
              // No need to compute velocity function.
      
              // Compute distance at t=5:
              double finalDistance = computeFinalDistance (0, 0, 0, 5, 0.01);
              System.out.println ("t=5  d=" + finalDistance);
      
              // Compute distance at t=10:
              finalDistance = computeFinalDistance (0, 0, 0, 10, 0.01);
              System.out.println ("t=10  d=" + finalDistance);
          }
          
      
          public static double computeFinalDistance (double  initialVelocity, double initialDistance, double initialTime, double endTime, double s)
          {
              // Constant acceleration.
              double a = 4.9;               
      
              // Set initial values.
              double v = initialVelocity;    
              double d = initialDistance;
              double t = initialTime;
      
              while (t < endTime) {
                  // First update distance:
                  d = d + s * v;
                  // Then update velocity:
                  v = v + s * a;
                  // Update time:
                  t = t + s;
              }
      
              return d;
          }
      
      }
              
    • It turns out that both are slightly different algorithms: one is better than the other in certain situations
           => The formal study of this type of question is called Numerical Analysis.
    • The second came first historically and is called the Euler Algorithm.
    • The first method is sometimes called Euler-Cromer Algorithm.

Exercise 16: What could you do experimentally to determine which of Euler or Euler-Cromer is better?

Finally, let's recall that we had a simulator for the Incline problem:

  • Let's revisit that program: (source file)
    public class InclineSimulator {
    
        // Mass and angle.
        public double mass = 1;
        public double angle = 30;
    
        // Acceleration, velocity, distance, and time.
        double a = 0;
        double v = 0;
        double d = 0;
        double t = 0;
    
        // Each time step.
        double delT = 0.001; 
    
        // We'll explain "g" later.
        double g = 9.8;
    
    
        public double run (double stopTime)
        {
            // Acceleration along incline: g*sin(alpha).
            double angleRadians = 2*Math.PI*angle / 360.0;
            a = g * Math.sin(angleRadians);
    
    	// Initialize variables.
            v = 0;
            d = 0;
            t = 0;
    
    	while (true) {
    	    // Update time.
    	    t += delT;
    
                // Increase velocity according to (constant) acceleration a.
    	    v = v + delT * a;
    
                // Increase distance according to velocity.
                d = d + delT * v;
    
    	    if (t >= stopTime) {
                    break;
    	    }
    	}
    
            // This is the distance moved in time t=stopTime.
            return d;
        }
    
    
        public double getV ()
        {
            return v;
        }
        
    
        public double getA ()
        {
            return a;
        }
    
        public double getX ()
        {
            double angleRadians = 2*Math.PI*angle / 360.0;
            return d * Math.cos (angleRadians);
        }
        
        public double getY ()
        {
            double angleRadians = 2*Math.PI*angle / 360.0;
            return d * Math.sin (angleRadians);
        }
    
    }
         

Exercise 17: Examine InclineSimulator above and answer these questions:

  • Is this the Euler or Euler-Cromer Algorithm at work?
  • Is the acceleration changing with time?
  • The while-loop has a slightly different structure. Any reason to prefer this approach or the earlier approach?

Coordinates

Let's return to the Projectile.java example from earlier:

  • Recall that the ball moves along a curve.

  • Suppose we try to analyze the distance moved along the curve:
         => Let d(t) = distance moved along curve.

  • Let's ask whether our "universal law" holds
         => Is velocity linear? Is acceleration constant?

  • For this purpose, we will use a simulation of the projectile, written in ProjectileSimulator.java.

  • Thus, to plot the distance travelled (the d(t) function): (source file)
    public class ProjectileSimulatorExample {
    
        public static void main (String[] argv)
        {
            // Make a new simulator object.
            ProjectileSimulator proSim = new ProjectileSimulator ();
    
            // We want to plot d vs. t
            Function dist = new Function ("distance");
    
            for (double t=0.1; t<=2.3; t+=0.1) {
                // mass=1, angle=37, initVel=20, s=0.01
                double d = proSim.run (1, 37, 20, t, 0.01);
                dist.add (t, d);
            }
    
            // Display.
            dist.show ();
        }
    
    }
         

Exercise 18: Download ProjectileSimulator.java and then modify ProjectileSimulatorExample.java to compute the velocity function v(t). Use s=0.01 and, for the derivative, use v(t)=(v(t+0.1) - v(t)) / 0.1. What can you conclude about the rate of change of velocity?

We will now explore one key insight in the development of physics:

  • Consider a bead coming down an incline, as we've seen in our examples:

    • Suppose we arranged light sources so that shadows of the bead are projected on the x and y-axes.
    • The figure above shows the direction in which the shadows would move
           => Rightwards for the x-axis shadow, downwards for the y-axis shadow.

  • Suppose we observe only the x-axis shadow:
    • The shadow is like a moving object.
    • It has a distance, velocity and acceleration functions that we could measure.

  • Similarly, we could observe the y-axis shadow as an independent object and measure its distance, velocity and acceleration functions.

Exercise 19: Execute Incline.java and observe the shadow objects moving. Are you able to say anything about whether the shadow objects satisfy our "universal law"?

Next, let's use our incline-simulator to obtain a distance function along the x-axis:

  • The simulator allows you to obtain distance moved by the x-axis shadow at any time.

  • Here's how we'd use it: (source file)
    public class InclineSimulatorExample2 {
    
        public static void main (String[] argv)
        {
            // Make a new instance of the class.
            InclineSimulator sim = new InclineSimulator ();
    
            // Set mass and angle.
            sim.mass = 1;
            sim.angle = 30;
            
            // Measure x(t) = distance moved along x-axis.
            Function dist = new Function ("dist");
    
            for (double t=1; t<=10; t+=1) {
                sim.run (t);
                double x = sim.getX ();
                dist.add (t, x);
            }
            
            // Display result.
            dist.show ();
        }
    
    }
        

Exercise 20: Download and execute the above code. Then modify to compute the velocity of the x-axis shadow. What do you observe?

Let's see what we get for the projectile problem:

  • Once again, we'll observe the shadows (x and y) of the ball.

  • Suppose we track the x-axis shadow as follows: (source file)
    public class ProjectileSimulatorExample2 {
    
        public static void main (String[] argv)
        {
            // Make a new simulator object.
            ProjectileSimulator proSim = new ProjectileSimulator ();
    
            // We want to plot d vs. t along x-axis.
            Function dist = new Function ("distance");
    
            for (double t=0.1; t<=2.3; t+=0.1) {
                // mass=1, angle=37, initVel=20, s=0.01
                proSim.run (1, 37, 20, t, 0.01);
                // After the simulation is run, get the final x-value.
                double x = proSim.getX ();
                dist.add (t, x);
            }
    
            // Display.
            dist.show ();
        }
    
    }
        

Exercise 21: Execute the above program. What do you notice about the distance function for the x-axis shadow? Can you explain?

Exercise 22: Modify the above code to compute and display the distance function for the y-axis shadow. Do the x-axis and y-axis shadows satisfy our "universal law"?

Reconstructing motion using "laws along x and y":

  • So far, it seems, the motion of the x or y shadows follows our universal "law" of constant acceleration.

  • Thus, for a moving object, we can say that its shadows have this property.

  • Does it work in reverse?
    • Does every pair of x-y shadows correspond to a unique location?
    • If the pair of shadows move according to our "law" will that result in natural motion of the resulting "unique" object?

  • Let's go back to the incline problem for a moment:

  • Suppose the bead has moved from d(t) to d(t+s) in a small time interval s.
    • Recall: v(t) = (d(t+s) - d(t)) / s (when s is small).
    • Similarly, a(t) = (v(t+s) - v(t)) / s.

  • Now, consider what happens to the shadow along the x-axis:
    • Let x(t) = distance moved by shadow in time t.
    • Let vx(t) = instantaneous velocity of x-shadow at time t.
    • Then, vx(t) = (x(t+s) - x(t)) / s.

  • Can we relate the two quantities v(t) and vx(t)?
    • Notice that cos(a) = (x(t+s) - x(t)) / (d(t+s) - d(t))
    • Thus, vx(t) = cos(a) (d(t+s) - d(t)) / s.
    • And so, vx(t) = v(t) cos(a).

  • Does this work for acceleration?
    • Let ax(t) = instantaneous acceleration of x-shadow at time t.
    • By definition, ax(t) = (vx(t+s) - vx(t)) / s.
    • Exactly the same argument applied to ax(t) gives us: ax(t) = a(t) cos(a)

  • Thus, the relationship between equivalent quantities is linear:

    • The constant of linearity depends on the angle.
           => The constant is exactly the cosine of the angle.

  • If we know x(t) and the angle a, we can completely reconstruct d(t).

  • Alternatively, if we know x(t) and y(t), we can completely reconstruct d(t).

    Exercise 23: Why are these statements true? And why is the second equivalent to the first?

  • Here is the reconstruction algorithm:
    • Apply the "law" or "laws" to determine x(t)
           => This gives us the distance function of the x-shadow.
    • Separately apply the "law" or "laws" to determine y(t)
           => This gives us the distance function of the y-shadow.
    • The combination (x(t), y(t)) determines the actual location of the object.

  • This is indeed how we constructed the motion of the projectile, for example, in ProjectileSimulator.java:
    
    public class ProjectileSimulator {
    
        // ... stuff left out ...
    
    
        public double run (double mass, double angle, double initialVelocity, double endTime, double timeInterval)
        {
            // ... initialization code not shown here ...
    
            while (t < endTime) {
                // Apply g to vy. Note: vx stays the same.
                vy = vy - g * delT;
                
                // Apply vx to x.
                double nextX = x + vx * delT;
                
                // Apply vy to y.
                double nextY = y + vy * delT;
    
                // Update total distance travelled.
                d = d + distance (x,y, nextX, nextY);
    
                // Set new (x,y)
                x = nextX;
                y = nextY;
    
                t = t + delT;
            }
            
            return d;
        }
        
        // ... other stuff not shown ...
    
    }
        

Exercise 24: Explain how d(t) is estimated in the above code? That is, why does

            d = d + distance (x,y, nextX, nextY);
   
work? Why did we need the variables nextX, nextY?

Can we similarly construct an (x,y) version for the incline problem?

  • First, let's look at the current version of the simulator for the incline problem: (source file)
    public class InclineSimulator {
    
        // ... stuff left out (not shown) ...    
    
        public double run (double stopTime)
        {
            // ... initialization not shown ...
    
    	while (true) {
    	    // Update time.
    	    t += delT;
    
                // Increase velocity accordingly.
    	    v = v + delT * a;
    
                // Increase distance according to velocity.
                d = d + delT * v;
    
    	    if (t >= stopTime) {
                    break;
    	    }
    	}
    
            return d;
        }
    
    }
         
    Note:
    • This version merely updates v(t) and d(t) as we would expect.
    • Recall: d(t) is distance moved along the incline.
    • Keep in mind that a(t) = a is fixed (constant).
           => When angle = 30o, it turns out that a = 4.9.
           (Why this is so will become clear later)

  • OK, now let's look an alternative way to write the incline-simulator: (source file)
    public class InclineSimulatorXY {
    
        // Mass and angle.
        public double mass = 1;
        public double angle = 30;
    
        // Separate acceleration, velocity and distance for x and y shadows.
        double ax = 0, ay = 0;
        double vx = 0, vy = 0;
        double x = 0, y = 0;
    
        // Time and time-step.
        double t = 0;
        double delT = 0.001; 
    
        // Vertical acceleration.
        double g = 9.8;
    
    
        public double run (double stopTime)
        {
            // Acceleration along incline: g*sin(alpha).
            double angleRadians = 2*Math.PI*angle / 360.0;
            double a = g * Math.sin(angleRadians);
    
    	// Initialize variables.
            vx = 0;  vy = 0;
            x = 0;  y = 0;
            t = 0;
    
            // We'll track total distance traveled along incline.
            double d = 0;
    
    	while (true) {
    	    // Update time.
    	    t += delT;
    
                // Acceleration along x and y.
                ax = a * Math.cos (angleRadians);
                ay = a * Math.sin (angleRadians);
    
                // Increase velocity according to appropriate acceleration.
                vx = vx + delT * ax;
                vy = vy - delT * ay;
    
                // Increase distance in each direction according to appropriate velocity.
                double nextX = x + delT * vx;
                double nextY = y + delT * vy;
    
                // Update total distance traveled.
                d = d + Math.sqrt ((nextX-x)*(nextX-x) + (nextY-y)*(nextY-y));
    
                x = nextX;
                y = nextY;
    
    	    if (t >= stopTime) {
                    break;
    	    }
    	}
    
            return d;
        }
    
    }
         
    Note: observe how the appropriate variables are computed for the x-shadow:
                ax = a * Math.cos (angleRadians);
           
    • We know that the acceleration along the incline is a (from the previous version of the inclinesimulator).
    • Our reasoning showed that acceleration along the x-direction (for the x-shadow) is ax(t) = a(t) cos(angle).
    • Once we know ax(t)
           => we can compute vx(t)
           => From which we can compute x(t) (distance moved along the x-axis).

Exercise 25: This clean "separation" into x and y shadows appears mystifying. Do you believe it works? Download and execute InclineSimulatorExample3.java to compare the two simulators. Print out the horizontal and vertical accelerations.


Some Unfinished Business

What exactly is g?

  • We know that a dropping (or even a tossed) ball experiences a downward "pull"
         => Informally, we refer to this as gravity.

  • Our simulations show that the acceleration is constant and that the constant is g = 9.8.

  • We've used this fact in several simulations:
    • In BallDropSimulator and BallTossSimulator:
      	    // Update time.
      	    t += delT;
      
                  // Decrease velocity by downward acceleration.
      	    vy = vy - delT * g;
      
                  // Increase distance (y) according to velocity (which will reduce height)
                  y = y + delT * vy;
                
    • In ProjectileSimulator:
                  // Apply g to vy. Note: vx stays the same.
                  vy = vy - g * delT;
                

  • The question we've avoided so far: what causes this acceleration? Why is it constant?

Exercise 26: Are there more unanswered questions regarding gravity?

Unfinished business relating to the incline problem:

  • We saw that there is acceleration in the x-direction.
         => What exactly is causing this acceleration? Who's doing the "pushing" in that direction?

  • Why isn't the vertical acceleration simply g?

Exercise 27: Do you have answers for the above two questions? As it turns out they are quite significant.

Other unanswered questions:

  • We haven't talked about units. In what units do we get g=9.8? What about units for velocity?

  • We've all heard the term "force" used in connection with the laws of motion. How does "force" relate to what we've discussed?

Problem Solving

Let's start by asking a few questions about the Ball-Drop problem:

  • What is the final velocity when the ball hits the ground after being dropped from a height of 1000?
  • At what height should one drop the ball to get a final velocity of approximately 200?
  • At what time and height is the velocity half the final velocity (when dropped from a height of 1000)?

Now let's tackle these questions:

  • First, we'll start by modifying the simulator to return the final velocity instead of time: (source file)
    public class BallDropSimulator2 {
    
        double vy;
        double t;
    
    
        public double run (double height)
        {
    	// Initialize variables.
            double a = 9.8;
    
            // We'll use "y" for distance (falling along y-axis) and "vy" for velocity.
            double y = height;
            vy = 0;
    
            t = 0;
            double delT = 0.001;
    
    	while (true) {
    	    // Update time.
    	    t += delT;
    
                // Decrease velocity by downward acceleration.
    	    vy = vy - delT * a;
    
                // Increase distance (y) according to velocity (which will reduce height)
                y = y + delT * vy;
    
    	    if (y <= 0) {   // Stop if it's hit the ground.
                    break;
    	    }
    	}
    
            return y;
        }
    
    
        public double getV ()
        {
            return vy;
        }
    
    
        public double getT ()
        {
            return t;
        }
    
    }
         
    Note:
    • We've created methods to return the final velocity and the time at which the ball hits the ground.
    • To do this, we've had to make these variables global.
    • We've increased the accuracy slightly (from 0.01 to 0.001).

  • Now let's write a separate program to use the simulator and answer the first question: (source file)
    public class BallDropSimulatorExercise {
    
        public static void main (String[] argv)
        {
            BallDropSimulator2 sim = new BallDropSimulator2 ();
    
            // Drop the ball from a height of 1000.
            sim.run (1000);
            
            // Obtain final velocity and time taken for the fall.
            double finalVelocity = sim.getV ();
            double timeTaken = sim.getT ();
    
            System.out.println ("finalVelocity=" + finalVelocity + " time=" + timeTaken);
        }
        
    }
        

  • To answer the second question, we will try different height values systematically: (source file)
    public class BallDropSimulatorExercise2 {
    
        public static void main (String[] argv)
        {
            BallDropSimulator2 sim = new BallDropSimulator2 ();
    
            // Drop the ball from a height of 1000.
            double height = 1000;
            sim.run (height);
            
            double finalVelocity = sim.getV ();
    
            while ( Math.abs (finalVelocity + 200) > 1 ) {
                // Try different heights systematically.
                height += 1;
                sim.run (height);
                finalVelocity = sim.getV ();
            }
    
            System.out.println ("Height required: " + height);
        }
        
    }
        
    Note:
    • Because velocities are negative (going down), the difference the velocity and 200 has a + sign.

Exercise 28: How many times is the loop iterated? Can you think of a more efficient algorithm for the same problem?

Exercise 29: Modify the code to answer the third question: At what time and height is the velocity half the final velocity (when dropped from a height of 1000)?

Next, let's look at the ball-tossing problem (toss a ball up with some initial velocity) and ask these questions:

  • What is the maximum height reached by the ball?
  • What initial velocity is needed to reach a height of 1000?
  • Suppose the ball is launched with an initial velocity of 50, and suppose we observe the velocity at a height of 100. Is the velocity the same going up as it is going down at this height?

Let's answer some of these questions now:

  • First, we'll modify the simulator to return the velocity: (source file)
    public class BallTossSimulator2 {
    
        // We made this a global variable ...
        double vy;
    
        public double run (double stopTime, double initVelocity)
        {
    	// Initialize variables.
            double a = 9.8;
            vy = initVelocity;
            double y = 0;
            double t = 0;
            double delT = 0.01;
    
    	while (true) {
    	    // Update time.
    	    t += delT;
    
                // Increase velocity accordingly.
    	    vy = vy - delT * a;
    
                // Increase distance according to velocity.
                y = y + delT * vy;
    
    	    if ((t >= stopTime) || (y <= 0)) {
                    break;
    	    }
    	}
    
            return y;
        }
    
        
        public double getV ()
        {
            // ... so that it's accessible here.
            return vy;
        }
    
    }
        

  • Notice the input parameters to the run() method:
    • One parameter is the length of the simulation (which might stop before the ball hits the ground).
    • The other is the launch velocity.

  • How do we know how much extra time to give (to allow for the ball to come down and hit the ground)?
         => We don't - so let's find out by writing a simple program: (source file)
    public class BallTossExercise {
    
        public static void main (String[] argv)
        {
            // Make an instance of our new simulator.
            BallTossSimulator2 sim = new BallTossSimulator2 ();
    
            // We'll start by trying 1 second.
            double stopTime = 1;
            double y = sim.run (stopTime, 50);
    
            // As long as we haven't hit ground, keep trying higher values of t.
            while (y > 0) {
                stopTime += 10;
                y = sim.run (stopTime, 50);
            }
    
            // This should be sufficient:
            System.out.println ("stopTime=" + stopTime + " y=" + y);
        }
    
    }
        
    (Turns out: 12 seconds is enough).

  • OK, next let's find the maximum height reached:
    • How do we do this?
    • Here's one idea: we try different stop-time's until, in two successive attempts, the height decreases.
    Here's the program: (source file)
    public class BallTossExercise2 {
    
        public static void main (String[] argv)
        {
            // Make an instance of our new simulator.
            BallTossSimulator2 sim = new BallTossSimulator2 ();
    
            // Find the height reached at t=1 and t=2
            double stopTime = 1;
            double prevY = sim.run (stopTime, 50);
            stopTime = 2;
            double nextY = sim.run (stopTime, 50);
    
            while (nextY > prevY) {
                // As long as y(t+1) > y(t), repeat.
                prevY = sim.run (stopTime, 50);
                stopTime = stopTime + 1;
                nextY = sim.run (stopTime, 50);
            }
    
            // Now we're sure that y(t+1) < y(t)
            System.out.println ("stopTime=" + stopTime + " prevY=" + prevY + " nextY=" + nextY);
        }
    
    }
    
         

  • We can add to this code to find the velocity needed to reach a height of 1000: (source file)
    public class BallTossExercise3 {
    
        public static void main (String[] argv)
        {
            for (double initVelocity=50; initVelocity<500; initVelocity+=50) {
                // Try this initial velocity:
                double heightReached = getMaxHeight (initVelocity);
                if (heightReached > 1000) {
                    // If we've exceeded 1000, then stop.
                    System.out.println ("initV=" + initVelocity + "  height=" + heightReached);
                    break;
                }
            }
        }
        
    
        static double getMaxHeight (double initVelocity)
        {
            // Make an instance of the simulator.
            BallTossSimulator2 sim = new BallTossSimulator2 ();
    
            // Find the height reached at t=1 and t=2
            double stopTime = 1;
            double prevY = sim.run (stopTime, initVelocity);
            stopTime = 2;
            double nextY = sim.run (stopTime, initVelocity);
    
            while (nextY > prevY) {
                // As long as y(t+1) > y(t), repeat.
                prevY = sim.run (stopTime, initVelocity);
                stopTime = stopTime + 1;
                nextY = sim.run (stopTime, initVelocity);
            }
    
            // Now we're sure that y(t+1) < y(t)
            System.out.println ("initV=" + initVelocity + "  stopTime=" + stopTime + " prevY=" + prevY + " nextY=" + nextY);
            return prevY;
        }
    
    }
         
    Note:
    • We have made a method out of the sub-problem of "finding the maximum height for a given initial velocity".
    • This sub-problem is then called repeatedly in steps of 50.

Exercise 30: How would you get a more accurate estimate of the initial velocity needed to reach 1000?

Next we will address the third question: is the velocity the same going up and down at a given height?

  • We will first modify the simulator as follows:
    • We will stop when a given "target height" is reached.
    • We will decide whether this height has been reached before or after the maximum height has been reached.

  • The input parameters now accept the target height, and whether or not one should reach this target before or after the maximum: (source file)
    public class BallTossSimulator3 {
    
        double vy;
    
        public double run (double stopTime, double initVelocity, double targetHeight, boolean beforeMaxHeight)
        {
    	// Initialize variables.
            double a = 9.8;
            vy = initVelocity;
            double y = 0;
            double t = 0;
            double delT = 0.01;
    
            boolean maxReached = false;
    
    	while (true) {
    	    // Update time.
    	    t += delT;
    
                // Increase velocity accordingly.
    	    vy = vy - delT * a;
    
                // Increase distance according to velocity.
                double nextY = y + delT * vy;
    
                double prevY = y;
                y = nextY;
    
                if (y < prevY) {
                    maxReached = true;
                }
                
                if (beforeMaxHeight) {
                    if (y > targetHeight) {
                        // Done.
                        break;
                    }
                }
                else if (maxReached) {
                    if (y < targetHeight) {
                        // Done.
                        break;
                    }
                }
                
    	    if ((t >= stopTime) || (y <= 0)) {
                    break;
    	    }
    	}
    
            return y;
        }
    
        
        public double getV ()
        {
            return vy;
        }
    
    }
         
    Note:
    • We use the same idea (observing successive y values) to see if the maximum has been reached.
    • We wait for "y > target" in the "before" case, and for "y < target" in the "after" case.

  • It's easy now to check the "before" and "after" velocities: (source file)
    public class BallTossExercise4 {
    
        public static void main (String[] argv)
        {
            // Make an instance of modified simulator.
            BallTossSimulator3 sim = new BallTossSimulator3 ();
    
            sim.run (1000, 50, 100, true);
            double beforeV = sim.getV ();
            sim.run (1000, 50, 100, false);
            double afterV = sim.getV ();
            
            System.out.println ("before: " + beforeV + "  after: " + afterV);
        }
    
    }
         

Exercise 31: So, is it true that the two velocities are the same? Is there an intuition for why or why not?

Consider these questions about the projectile problem:

  • For a fixed velocity of 50, which angle results in the farthest horizontal distance traveled?
         => This is an age-old ballistics problem

  • Suppose we want the projectile to hit a target at a distance of 200 from the launch point. Find two combinations of angle and initial velocity to achieve this goal. Of the two, which one has a smaller time-of-flight?

  • We'll begin by re-writing the simulator slightly: (source file)
    public class ProjectileSimulator2 {
    
        double x=0, y=0;             // Track (x,y) 
        double vx, vy;               // Velocity components
    
    
        public double run (double angle, double initialVelocity) 
        {
            // Initialize values.
            double delT = 0.001;                      // Interval of time.
            double t = 0;                             // Current time.
            double g = 9.8;                           // 9.8 N 
            double theta = Math.toRadians (angle);    // Convert degrees to radians.
    
            x = 0;
            y = 0;
            
            // Initial vx = component of initialVelocity in x-direction.
            vx = initialVelocity * Math.cos (theta);
    
            // Initial vy = component of initialVelocity in y-direction.
            vy = initialVelocity * Math.sin (theta);
    
            while (y >= 0) {
                // Apply g to vy. Note: vx stays the same.
                vy = vy - g * delT;
                
                // Apply vx to x.
                x = x + vx * delT;
                
                // Apply vy to y.
                y = y + vy * delT;
    
                t = t + delT;
            }
            
            return t;
        }
        
    
        public double getX ()
        {
            return x;
        }
    
    }
         
    Note:
    • The input parameters are: launch angle and velocity.
    • The simulator returns the time taken for the flight.
    • There is a method getX() that we can call to obtain the horizontal distance traveled.

  • Next, let's use the initial velocity of 50, and try different angles: (source file)
    public class ProjectileExercise {
    
        public static void main (String[] argv)
        {
            // Make an instance of our new simulator.
            ProjectileSimulator2 sim = new ProjectileSimulator2 ();
    
            // Launch velocity is 50.
            double initV = 50;
            
            // Try angles in the range [10,80]
            for (double angle=10; angle<=80; angle+=1) {
                sim.run (angle, initV);
                double x = sim.getX ();
                System.out.println ("angle=" + angle + " x=" + x);
            }
    
        }
    
    }
         

  • A slightly more elegant approach would be to record the the best result in the loop: (source file)
    public class ProjectileExercise2 {
    
        public static void main (String[] argv)
        {
            // Make an instance of our new simulator.
            ProjectileSimulator2 sim = new ProjectileSimulator2 ();
    
            // Launch velocity is 50.
            double initV = 50;
            
            // Try angles in the range [10,80]
            double bestAngle = 10;
            double bestX = 0;
            
            for (double angle=10; angle<=80; angle+=1) {
                sim.run (angle, initV);
                double x = sim.getX ();
                if (x > bestX) {
                    bestAngle = angle;
                    bestX = x;
                }
            }
    
            System.out.println ("Best angle: " + bestAngle);
        }
    
    }
         

Exercise 32: What is the best angle? Run the program and find out. Does it make intuitive sense?

Exercise 33: Write code to solve the second problem: find two angle/velocity combinations to reach a target at a distance of 200. What is the time-of-flight for each?


Problem Solving II

Consider the following problem:

  • There are two regions or land parcels, each with a different surface.
  • We are to drive a vehicle from point A to point B below (bird's eye view):

    However, we are to observe the following rules:

    • The maximum velocity in the first region is: v1
    • The maximum velocity in the second region is: v2

  • The obvious question is: what route should one take to reach in least possible time?

Exercise 33: Is it obvious that the route within each region must be a straight line?

Let us draw the configuration on the x-y plane:

  • We'll put A on the y-axis, and B on the x-axis:

  • Let P be a candidate crossing point.

Exercise 34: Compute by hand the time taken to go from A to P, and the time taken to go from P to B in terms of a, b, v1, v2, x, y. Then download and modify TwoVelocityProblem.java and implement your formulas as code. Compare the program's results with your hand-worked results.

Exercise 35: Suppose v1 is (much) smaller than v2. Which of the three routes shown below is likely to be best?

Next, we will start to solve the optimization problem:

  • First, some notation:

    • d1 = distance traveled in first phase.
    • d2 = distance traveled in second phase.
    • (a-y) = vertical distance in first phase
    • y = vertical distance in second phase

  • Since the border is fixed, x is fixed.
         => We only need to find the best possible y for the point P = (x,y).

Exercise 36: Then download and modify TwoVelocityProblem2.java to try different values of y in a loop.

Next, let's see if we can discover the "law" at work:

  • We will vary the ratio v2 / v1.

  • For each possible ratio, we want to identify the best y value.

  • We would like to discover the relationship between the ratio v2 / v1 ... and something related to the problem parameters.

Exercise 37: Then download and modify TwoVelocityProblem3.java to try different values of y in a loop, and also compare with the distances d1, d2, (a-y), y. Notice that we are using different ratios v2 / v1.

  • Is there a relationship between v2/v1 and d2/d1?
  • Between v2/v1 and (a-y)/y?

Now we will consider the case, when velocity is NOT constant:

  • Consider a bead sliding down a wire.

    • Suppose that we can use two straight-line segments.
    • Suppose also that the "kink" doesn't slow down the bead.
    Is the fastest way down the straight line (in black)? Or is it faster to use two segments of different slopes?

  • Without loss of generality, we will assume the starting point is (x1,y1) and that the ending point is (x3,y3).

  • We are free to choose the "turning" point (x2,y2)

Exercise 38: Before playing with a simulation, what does your intuition tell you?

  • Download and execute MultiIncline.java, which lets you define the number of segments and move them around. Were you able to get bead down sooner than using a straight line?
  • What does the phrase "without loss of generality" mean in the context above? What "general" situations might we be interested in?
  • Referring to the figure below,

    write down the sin and cosine of the two angles shown in terms of the point coordinates.

We'll start by re-writing our incline simulator to simulate a single segment:

  • A segment will be defined by the end points (x1,y1) (left and above), and (x2,y2) (right and below).

  • Here's the code: (source file)
    public class InclineSegmentSimulator {
    
        // Separate acceleration, velocity and distance for x and y shadows.
        double ax = 0, ay = 0;
        double vx = 0, vy = 0;
        double x = 0, y = 0;
    
        // Time and time-step.
        double t = 0;
        double delT = 0.001; 
    
        // Vertical acceleration.
        double g = 9.8;
        
    
        public double run (double x1, double y1, double x2, double y2, double initvx, double initvy)
        {
            // Acceleration along incline: g*sin(alpha).
            double hyp = distance (x1,y1, x2,y2);
            double xDiff = Math.abs (x1-x2);
            double yDiff = Math.abs (y1-y2);
            double sineOfAngle = xDiff / hyp;
            double cosineOfAngle = yDiff / hyp;
    
            // Acceleration along incline.
            double a = g * sineOfAngle;
    
            // Acceleration along x and y.
            ax = a * cosineOfAngle;
            ay = a * sineOfAngle;
    
    	// Initialize velocity components.
            vx = initvx;  vy = initvy;
    
            // Distance components.
            x = x1;  y = y1;
    
            t = 0;
    
    	while (true) {
    	    // Update time.
    	    t += delT;
    
                // Increase velocity according to appropriate acceleration.
                vx = vx + delT * ax;
                vy = vy - delT * ay;
    
                // Increase distance in each direction according to appropriate velocity.
                x = x + delT * vx;
                y = y + delT * vy;
    
    	    if ((x > x2) && (y < y2)) {
                    // NOTE: this assumes that (x2,y2) is to the right and below.
                    break;
    	    }
    	}
    
            return t;
        }
    
    
        double distance (double x1, double y1, double x2, double y2)
        {
    	return Math.sqrt ((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
        }
    
    
        public double getVx ()
        {
            return vx;
        }
        
    
        public double getVy ()
        {
            return vy;
        }
        
    }
        
    Note:
    • Compare the calculation of the angle's sine/cosine with your hand computations.
    • Notice that the initial velocity is assumed to be given as input.
    • The simulator is written to return the total time taken down the incline.

  • Let's use this simulator as follows for a two-segment problem: (source file)
    public class TwoSegmentIncline {
    
        public static void main (String[] argv)
        {
            // Make an instance of the simulator.
            InclineSegmentSimulator sim = new InclineSegmentSimulator ();
    
            // Run the straight incline as one segment.
            double t = sim.run (0, 10, 10, 0, 0, 0);
            System.out.println ("Straight solution: " + t);
    
            // Now, a potential two segment solution. First segment:
            double t1 = sim.run (0, 10, 5, 5, 0, 0);
    
            // Get time taken for the second segment:
    
            double t2 = ...
    
            // Comparison:
            System.out.println ("Twisted solution: " + (t1+t2));
        }
    
    }
         

Exercise 39: Download TwoSegmentIncline.java and InclineSegmentSimulator.java Fill in the missing code above in InclineSegmentSimulator. What reasoning did you use in the missing code? What can you say about the underlying principle?

Next, let's see if we can find a faster two-segment solution:

  • Suppose we fix x2=5 above (the middle of the incline).
         => We will try to find the best y2.

  • One simple way is to try various values: (source file)
    public class TwoSegmentIncline2 {
    
        public static void main (String[] argv)
        {
            // Make an instance of the simulator.
            InclineSegmentSimulator sim = new InclineSegmentSimulator ();
    
            // Run the straight incline as one segment.
            double t = sim.run (0, 10, 10, 0, 0, 0);
            System.out.println ("Straight solution: " + t);
    
            double x2 = 5;
            for (double y2=1; y2<=9; y2+=1) {
                // Now, a potential two segment solution. First segment:
                double t1 = sim.run (0, 10, x2, y2, 0, 0);
                
                double t2 = ...
    
                // Comparison:
                System.out.println ("Twisted solution for y2=" + y2 + ": " + (t1+t2));
            }
            
        }
    
        // ...
    
    }
        

Exercise 40: Fill in the missing code above.