Modeling

This chapter introduces the modeling framework we will use throughout the book, and works through our first example, using a simple model of physics to evaluate the claim that a penny falling from the height of the Empire State Building could kill someone if it hit them on the head. Also, you’ll see how to do computation in Python with units like meters and seconds.

This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. Click here to access the notebooks: https://allendowney.github.io/ModSimPy/.

The Modeling Framework

This book is about modeling and simulating physical systems. The following diagram shows what I mean by modeling:

Diagram of the modeling framework.

Starting in the lower left, the system is something in the real world we are interested in. To model the system, we have to decide which elements of the real world to include and which we can leave out. This process is called abstraction.

The result of abstraction is a model, which is a description of the system that includes only the features we think are essential. A model can be represented in the form of diagrams and equations, which can be used for mathematical analysis. It can also be implemented in the form of a computer program, which can run simulations.

The result of analysis and simulation might be a prediction about what the system will do, an explanation of why it behaves the way it does, or a design intended to achieve a purpose.

We can validate predictions and test designs by taking measurements from the real world and comparing the data we get with the results from analysis and simulation.

For any physical system, there are many possible models, each one including and excluding different features, or including different levels of detail. The goal of the modeling process is to find the model best suited to its purpose (prediction, explanation, or design).

Sometimes the best model is the most detailed. If we include more features, the model is more realistic, and we expect its predictions to be more accurate. But often a simpler model is better. If we include only the essential features and leave out the rest, we get models that are easier to work with, and the explanations they provide can be clearer and more compelling.

As an example, suppose someone asks you why the orbit of the Earth is elliptical. If you model the Earth and Sun as point masses (ignoring their actual size), compute the gravitational force between them using Newton’s law of universal gravitation, and compute the resulting orbit using Newton’s laws of motion, you can show that the result is an ellipse. Of course, the actual orbit of Earth is not a perfect ellipse, because of the gravitational forces of the Moon, Jupiter, and other objects in the solar system, and because Newton’s laws of motion are only approximately true (they don’t take into account relativistic effects). But adding these features to the model would not improve the explanation; more detail would only be a distraction from the fundamental cause. However, if the goal is to predict the position of the Earth with great precision, including more details might be necessary.

Choosing the best model depends on what the model is for. It is usually a good idea to start with a simple model, even if it is likely to be too simple, and test whether it is good enough for its purpose. Then you can add features gradually, starting with the ones you expect to be most essential. This process is called iterative modeling.

Comparing results of successive models provides a form of internal validation, so you can catch conceptual, mathematical, and software errors. And by adding and removing features, you can tell which ones have the biggest effect on the results, and which can be ignored.

Comparing results to data from the real world provides external validation, which is generally the strongest test.

The modeling framework is pretty abstract; the following example might make it clearer.

The Falling Penny Myth

You might have heard that a penny dropped from the top of the Empire State Building would be going so fast when it hit the pavement that it would be embedded in the concrete; or if it hit a person, it would break their skull.

We can test this myth by making and analyzing a model. To get started, we’ll assume that the effect of air resistance is small. This will turn out to be a bad assumption, but bear with me. If air resistance is negligible, the primary force acting on the penny is gravity, which causes the penny to accelerate downward.

If the initial velocity is 0 and the acceleration is \(a\), the velocity after \(t\) seconds is

\[v = a t\]

and the distance the penny has dropped is

\[x = a t^2 / 2\]

To find the time until the penny reaches the sidewalk, we can solve for \(t\):

\[t = \sqrt{ 2 x / a}\]

Plugging in the acceleration of gravity, \(a = 9.8\) m/s\(^2\), and the height of the Empire State Building, \(x = 381\) m, we get \(t = 8.8\) s.

Then computing \(v = a t\) we get a velocity on impact of \(86\) m/s, which is about 190 miles per hour. That sounds like it could hurt.

Of course, these results are not exact because the model is based on simplifications. For example, we assume that gravity is constant. In fact, the force of gravity is different on different parts of the globe, and it gets weaker as you move away from the surface. But these differences are small, so ignoring them is probably a good choice for this scenario.

On the other hand, ignoring air resistance is not a good choice. Once the penny gets to about 29 m/s, the upward force of air resistance equals the downward force of gravity, so the penny stops accelerating. This is the terminal velocity of the penny in air.

Once the penny reaches terminal velocity, it doesn’t matter how much farther it falls; it hits the sidewalk at about 29 m/s. That’s much less than 86 m/s, as the simple model predicts.

The statistician George Box famously said “All models are wrong, but some are useful.” He was talking about statistical models, but his wise words apply to all kinds of models. Our first model, which ignores air resistance, is very wrong, and probably not useful. The second model, which takes air resistance into account, is still wrong, but it’s better, and it’s good enough to refute the myth.

The television show Mythbusters has tested the myth of the falling penny more carefully; you can view the results at https://modsimpy.com/myth/. Their work is based on a mathematical model of motion, measurements to determine the force of air resistance on a penny, and a physical model of a human head.

Computation In Python

Let me show you how I computed the results from the previous section using Python. First we’ll create a variable to represent acceleration due to gravity in meters per second squared (m/s\(^2\)).

a = 9.8

A variable is a name that corresponds to a value. In this example, the name is a and the value is the number 9.8.

Suppose we let the penny drop for \(3.4\) seconds (s). I’ll create a variable to represent this time:

t = 3.4

Now we can compute the velocity of the penny after t seconds.

v = a * t

Python uses the symbol * for multiplication. The other arithmetic operators are + for addition, - for subtraction, / for division, and ** for exponentiation.

When you assign a value to a variable, Jupyter does not show the result automatically, but you can display the value of a variable like this:

v
33.32

After \(3.4\) s, the velocity of the penny is about \(33\) m/s (ignoring air resistance). Now let’s see how far it would travel during that time:

x = a * t**2 / 2
x
56.644

It would travel about \(56\) m. Now, going in the other direction, let’s compute the time it takes to fall 381 m, the height of the Empire State Building.

h = 381

For this computation, we need the square root function, sqrt, which is provided by a library called NumPy. Before we can use it, we have to import it like this:

from numpy import sqrt

Now we can use it like this:

t = sqrt(2 * h / a)
t
8.817885349720552

With no air resistance, it would take about \(8.8\) s for the penny to reach the sidewalk.

v = a * t
v
86.41527642726142

And its velocity on impact would be about \(86\) m/s.

False Precision

Python displays results with about 16 digits, which gives the impression that they are very precise, but don’t be fooled. The numbers we get out are only as good as the numbers we put in.

For example, the “roof height” of the Empire State Building is \(380\) m (according to Wikipedia: https://en.wikipedia.org/wiki/Empire_State_Building). I chose \(h=381\) m for this example on the assumption that the observation deck is on the roof and you drop the penny from a 1 meter railing. But that’s probably not right, so we should treat this value as an approximation where only the first two digits are likely to be right.

If the precision of the inputs is two digits, the precision of the outputs is two digits, at most. That’s why, if the output is 86.41527642726142, I report it as “about 86”.

Computation With Units

The computations we just did use numbers without units. When we set h=381, we left out the meters, and when we set a=9.8, we left out the meters per second squared. And, when we got the result v=86, we added back the meters per second.

Leaving units of out computation is common practice, but it tends to cause errors, including the very expensive failure of the Mars Climate Orbiter in 1999 (see https://en.wikipedia.org/wiki/Mars_Climate_Orbiter). When possible, it is better to include units in the computation.

To represent units, we’ll use a library called Pint (https://pint.readthedocs.io/). To use it, we have to import a function called UnitRegistry and call it like this:

from pint import UnitRegistry

units = UnitRegistry()

The result is an object that contains variables representing pretty much every unit you’ve heard of. For example:

units.league
league
units.fortnight
fortnight

But leagues and fortnights are not SI units. Instead, we will use meter and second.

meter = units.meter
meter
meter
second = units.second
second
second

We can use meter and second to create a variable named a and give it the value of acceleration due to gravity.

a = 9.8 * meter / second**2
a
9.8 meter/second2

The result is a quantity with two parts, called magnitude and units, which we can access like this:

a.magnitude
9.8
a.units
meter/second2

We can also create a quantity that represents \(3.4\) s.

t = 3.4 * second
t
3.4 second

And use it to compute the distance a penny would fall after t seconds with constant acceleration a.

a * t**2 / 2
56.644 meter

Notice that the units of the result are correct. If we create a quantity to represent the height of the Empire State Building:

h = 381 * meter

We can use it to compute the time the penny would take to reach the sidewalk.

t = sqrt(2 * h / a)
t
8.817885349720552 second

And the velocity of the penny on impact:

v = a * t
v
86.41527642726142 meter/second

As in the previous section, the result is about \(86\), but now it has the correct units, m/s.

With Pint quantities, we can convert from one set of units to another like this:

mile = units.mile
hour = units.hour
v.to(mile/hour)
193.30546802805432 mile/hour

If you are more familiar with miles per hour, this result might be easier to interpret. And it might give you a sense that this model is not realistic.

Summary

This chapter introduces a modeling framework that consists of three steps:

  • Abstraction is the process of defining a model by deciding which elements of the real world to include and which can be left out.

  • Analysis and simulation are ways to use a model to generate predictions, explain why things behave as they do, and design things that behave as we want.

  • Validation is how we test whether the model is right, often by comparing predictions with measurements from the real world.

As a first example, we modeled a penny dropped from the Empire State building, including gravity but ignoring air resistance. In the exercises, you’ll have a chance to try a better model, including air resistance.

This chapter also presents Pint, a library for doing computation with units, which is convenient for converting between different units and helpful for avoiding catastrophic errors.

Exercises

Before you go on, you might want to work on the following exercises.

Exercise 1

In mathematical notation, we can write an equation like \(v = a t\) and it’s understood that we are multiplying \(a\) and \(t\). But that doesn’t work in Python. If you put two variables side-by-side, like this:

v = a t

you’ll get a syntax error, which means that something is wrong with the structure of the program. Try it out in the next cell so you see what the error message looks like.

a = 9.8 * meter / second**2
t = 3.4 * second

v = a * t
v
33.32 meter/second

Exercise 2

In this chapter we used the sqrt function from the NumPy library. NumPy also provides a variable named pi that contains an approximation of the mathematical constant \(\pi\). We can import it like this:

from numpy import pi
pi
3.141592653589793

NumPy provides other functions we’ll use, including log, exp, sin, and cos. Import sin and cos from NumPy and compute

\[sin^2 (\pi/4) + cos^2 (\pi/4)\]

Note: A mathematical identity tells us that the answer should be \(1\).

# Solution

from numpy import sin, cos

sin(pi/4)**2 + cos(pi/4)**2
1.0

Exercise 3

Suppose you bring a 10 foot pole to the top of the Empire State Building and use it to drop the penny from h plus 10 feet.

Define a variable named foot that contains the unit foot provided by UNITS. Define a variable named pole_height and give it the value 10 feet.

What happens if you add h, which is in units of meters, to pole_height, which is in units of feet? What happens if you write the addition the other way around?

h = 381 * meter
# Solution

foot = units.foot
pole_height = 10 * foot

h + pole_height
384.048 meter
# Solution

pole_height + h
1260.0 foot

Exercise 4

Why would it be nonsensical to add a and t? What happens if you try?

a = 9.8 * meter / second**2
t = 3.4 * second

In this example, you should get a DimensionalityError, which indicates that you have violated a rule of dimensional analysis: you cannot add quantities with different dimensions.

The error messages you get from Python are big and scary, but if you read them carefully, they contain a lot of useful information.

The last line usually tells you what type of error happened, and sometimes additional information, so you might want to start from the bottom and read up.

The previous lines are a traceback of what was happening when the error occurred. The first section of the traceback shows the code you wrote. The following sections are often from Python libraries.

Exercise 5

Suppose instead of dropping the penny, you throw it downward at its terminal velocity, \(29\) m/s. How long would it take to fall \(381\) m?

# Solution

h = 381 * meter
v = 29 * meter / second

t = h / v
t
13.137931034482758 second

Exercise 6:

So far we have considered two models of a falling penny:

  • If we ignore air resistance, the penny falls with constant acceleration, and we can compute the time to reach the sidewalk and the velocity of the penny when it gets there.

  • If we take air resistance into account, and drop the penny at its terminal velocity, it falls with constant velocity.

Now let’s consider a third model that includes elements of the first two: let’s assume that the acceleration of the penny is a until the penny reaches \(29\) m/s, and then \(0\) m/s afterwards. What is the total time for the penny to fall \(381\) m?

You can break this question into three parts:

  1. How long would the penny take to reach \(29\) m/s with constant acceleration a.

  2. How far would it fall during that time?

  3. How long would it take to fall the remaining distance with constant velocity \(29\) m/s?

Suggestion: Assign each intermediate result to a variable with a meaningful name. And assign units to all quantities!

a = 9.8 * meter / second**2
h = 381 * meter
# Solution

v_terminal = 29 * meter / second 
# Solution

t1 = v_terminal / a
print('Time to reach terminal velocity', t1)
Time to reach terminal velocity 2.9591836734693877 second
# Solution

h1 = a * t1**2 / 2
print('Height fallen in t1', h1)
Height fallen in t1 42.90816326530612 meter
# Solution

t2 = (h - h1) / v_terminal
print('Time to fall remaining distance', t2)
Time to fall remaining distance 11.658339197748065 second
# Solution

t_total = t1 + t2
print('Total falling time', t_total)
Total falling time 14.617522871217453 second

Exercise 7

When I was in high school, the pitcher on the baseball team claimed that when he threw a fastball he was throwing the ball down; that is, the ball left his hand at a downward angle. I was skeptical; watching from the side, I thought the ball left his hand at an upward angle.

Can you think of a simple model you could use to settle the argument? What factors would you include and what could you ignore? What quantities would you have to look up or estimate?

I suggest you convert all quantities to SI units like meters and seconds (see https://en.wikipedia.org/wiki/International_System_of_Units).

# Solution

# I suggest the following model: 

# 1. Let's ignore the motion of the ball toward home plate and
#    think about how far the ball would drop while it's in flight.

# 2. Let's ignore air resistance.  Since we are only thinking 
#    about the relatively slow motion in the vertical direction, 
#    this is probably a good assumption.

# 3. Let's also ignore the effect of spin.  This is probably a
#    less good assumption.

# The distance from the pitcher's mound to home plate is about 60
# feet, but the point where the ball is released is a bit closer.

# An average pitcher in high school might be able to throw a ball
# at 80 mph.
# Solution

v = (80 * mile / hour).to(meter/second)
x = (60 * foot).to(meter)
# Solution

t = x / v
t
0.5113636363636362 second
# Solution

a = 9.8 * meter / second**2
h = a * t**2 / 2
h
1.2813145661157022 meter
# Solution

# In the time it takes the ball to reach home plate, it drops
# about 1.3 m.  If the release point is at 2 m, which is plausible,
# it would cross the plate at 0.7 m, which is in the strike zone.

# So I could be wrong -- it is plausible that the ball leaves
# the pitcher's hand at a downward angle, at least for some pitches.

Exercise 8

Suppose I run a 10K race in 44:52 (44 minutes and 52 seconds). What is my average page in minutes per mile?

mile = units.mile
kilometer = units.kilometer
minute = units.minute
# Solution

t = 52 * second + 44 * minute
t
2692 second
# Solution

v = 10 * kilometer / t
v
0.003714710252600297 kilometer/second
# Solution

pace_per_km = (1 / v)
pace_per_km
269.2 second/kilometer
# Solution

pace_per_mile = pace_per_km.to(minute / mile)
pace_per_mile
7.220590080000001 minute/mile
# Solution

# To convert to minutes and seconds, we can use np.round.
# But we haven't covered that yet.

from numpy import round

round(pace_per_mile)
7.0 minute/mile
# Solution

remainder = pace_per_mile - round(pace_per_mile)
remainder.to(second/mile)
13.235404800000055 second/mile