Modeling and Simulation in Python Allen B Downey Instant Download
Modeling and Simulation in Python Allen B Downey Instant Download
download
https://ebookbell.com/product/modeling-and-simulation-in-python-
allen-b-downey-6855158
https://ebookbell.com/product/modeling-and-simulation-in-python-
converted-allen-b-downey-49550968
https://ebookbell.com/product/modeling-and-simulation-in-python-jason-
m-kinser-42344096
https://ebookbell.com/product/modeling-and-simulation-in-python-an-
introduction-for-scientists-and-engineers-allen-b-downey-48274994
https://ebookbell.com/product/modeling-and-simulation-in-python-allen-
b-downey-52556530
Modeling And Simulation In Python Version 23 Allen B Downey
https://ebookbell.com/product/modeling-and-simulation-in-python-
version-23-allen-b-downey-7236520
https://ebookbell.com/product/modeling-and-simulation-in-python-allen-
b-downey-59378552
https://ebookbell.com/product/simulation-with-python-develop-
simulation-and-modeling-in-natural-sciences-engineering-and-social-
sciences-1st-edition-rongpeng-li-44887188
https://ebookbell.com/product/handson-simulation-modeling-with-python-
develop-simulation-models-for-improved-efficiency-and-precision-in-
the-decisionmaking-process-2nd-edition-2nd-edition-giuseppe-
ciaburro-48708554
https://ebookbell.com/product/modeling-and-simulation-in-thermal-and-
fluids-engineering-krishnan-murugesan-48800954
Modeling and Simulation in Python
Version 1.0.2
Modeling and Simulation in Python
Version 1.0.2
Allen B. Downey
Permission is granted to copy, distribute, transmit and adapt this work under a
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
License: http://modsimpy.com/license.
The LATEX source and code for this book is available from
https://github.com/AllenDowney/ModSimPy
iv
Contents
Preface xi
0.1 Can modeling be taught? . . . . . . . . . . . . . . . . . . . . xiii
0.2 How much programming do I need? . . . . . . . . . . . . . . xiv
0.3 How much math and science do I need? . . . . . . . . . . . . xv
0.4 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . xvi
0.5 Installing Python and the libraries . . . . . . . . . . . . . . . xvi
0.6 Copying my files . . . . . . . . . . . . . . . . . . . . . . . . . xvii
0.7 Running Jupyter . . . . . . . . . . . . . . . . . . . . . . . . xviii
1 Modeling 1
1.1 The falling penny myth . . . . . . . . . . . . . . . . . . . . . 1
1.2 Computation . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3 Modeling a bike share system . . . . . . . . . . . . . . . . . 4
1.4 Plotting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.5 Defining functions . . . . . . . . . . . . . . . . . . . . . . . . 7
1.6 Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.7 Print statements . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.8 If statements . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.9 Optional parameters . . . . . . . . . . . . . . . . . . . . . . 14
1.10 For loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
vi CONTENTS
1.11 Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2 Simulation 17
2.1 Iterative modeling . . . . . . . . . . . . . . . . . . . . . . . . 17
2.2 More than one System object . . . . . . . . . . . . . . . . . 18
2.3 Documentation . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.4 Negative bikes . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.5 Comparison operators . . . . . . . . . . . . . . . . . . . . . . 23
2.6 Metrics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.7 Functions that return values . . . . . . . . . . . . . . . . . . 26
2.8 Two kinds of parameters . . . . . . . . . . . . . . . . . . . . 27
2.9 Loops and arrays . . . . . . . . . . . . . . . . . . . . . . . . 28
2.10 Sweeping parameters . . . . . . . . . . . . . . . . . . . . . . 29
2.11 Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3 Explanation 31
3.1 World population data . . . . . . . . . . . . . . . . . . . . . 31
3.2 Series . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.3 Constant growth model . . . . . . . . . . . . . . . . . . . . . 34
3.4 Simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.5 Now with System objects . . . . . . . . . . . . . . . . . . . . 38
3.6 Proportional growth model . . . . . . . . . . . . . . . . . . . 39
3.7 Factoring out the update function . . . . . . . . . . . . . . . 41
3.8 Combining birth and death . . . . . . . . . . . . . . . . . . . 42
3.9 Quadratic growth . . . . . . . . . . . . . . . . . . . . . . . . 43
3.10 Equilibrium . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
3.11 Disfunctions . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
CONTENTS vii
4 Prediction 51
4.1 Generating projections . . . . . . . . . . . . . . . . . . . . . 51
4.2 Comparing projections . . . . . . . . . . . . . . . . . . . . . 54
4.3 Recurrence relations . . . . . . . . . . . . . . . . . . . . . . 55
4.4 Differential equations . . . . . . . . . . . . . . . . . . . . . . 57
4.5 Analysis and simulation . . . . . . . . . . . . . . . . . . . . 59
4.6 Analysis with WolframAlpha . . . . . . . . . . . . . . . . . . 60
4.7 Analysis with SymPy . . . . . . . . . . . . . . . . . . . . . . 61
4.8 Differential equations in SymPy . . . . . . . . . . . . . . . . 62
4.9 Solving the quadratic growth model . . . . . . . . . . . . . . 63
4.10 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
5 Design 65
5.1 The Freshman Plague . . . . . . . . . . . . . . . . . . . . . . 65
5.2 The SIR model . . . . . . . . . . . . . . . . . . . . . . . . . 66
5.3 The SIR equations . . . . . . . . . . . . . . . . . . . . . . . 67
5.4 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 68
5.5 The update function . . . . . . . . . . . . . . . . . . . . . . 69
5.6 Running the simulation . . . . . . . . . . . . . . . . . . . . . 71
5.7 Collecting the results . . . . . . . . . . . . . . . . . . . . . . 71
5.8 Now with a TimeFrame . . . . . . . . . . . . . . . . . . . . . 73
5.9 Metrics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.10 Immunization . . . . . . . . . . . . . . . . . . . . . . . . . . 76
5.11 Hand washing . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.12 Optimization . . . . . . . . . . . . . . . . . . . . . . . . . . 82
6 Analysis 85
viii CONTENTS
6.1 Unpack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
6.2 Sweeping beta . . . . . . . . . . . . . . . . . . . . . . . . . . 86
6.3 Sweeping gamma . . . . . . . . . . . . . . . . . . . . . . . . 88
6.4 Nondimensionalization . . . . . . . . . . . . . . . . . . . . . 90
6.5 Contact number . . . . . . . . . . . . . . . . . . . . . . . . . 92
6.6 Analysis and simulation . . . . . . . . . . . . . . . . . . . . 94
7 Thermal systems 97
7.1 The coffee cooling problem . . . . . . . . . . . . . . . . . . . 97
7.2 Temperature and heat . . . . . . . . . . . . . . . . . . . . . 98
7.3 Heat transfer . . . . . . . . . . . . . . . . . . . . . . . . . . 99
7.4 Newton’s law of cooling . . . . . . . . . . . . . . . . . . . . . 100
7.5 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 101
7.6 Using fsolve . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
7.7 Mixing liquids . . . . . . . . . . . . . . . . . . . . . . . . . . 106
7.8 Mix first or last? . . . . . . . . . . . . . . . . . . . . . . . . 107
7.9 Optimization . . . . . . . . . . . . . . . . . . . . . . . . . . 108
7.10 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
8 Pharmacokinetics 113
8.1 The glucose-insulin system . . . . . . . . . . . . . . . . . . . 113
8.2 The glucose minimal model . . . . . . . . . . . . . . . . . . . 114
8.3 Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
8.4 Interpolation . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
8.5 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 119
8.6 Numerical solution of differential equations . . . . . . . . . . 122
8.7 Least squares . . . . . . . . . . . . . . . . . . . . . . . . . . 124
CONTENTS ix
9 Projectiles 131
9.1 Newton’s second law of motion . . . . . . . . . . . . . . . . . 131
9.2 Dropping pennies . . . . . . . . . . . . . . . . . . . . . . . . 133
9.3 Onto the sidewalk . . . . . . . . . . . . . . . . . . . . . . . . 136
9.4 With air resistance . . . . . . . . . . . . . . . . . . . . . . . 137
9.5 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 138
9.6 Dropping quarters . . . . . . . . . . . . . . . . . . . . . . . . 140
11 Rotation 159
11.1 The physics of toilet paper . . . . . . . . . . . . . . . . . . . 160
11.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 161
11.3 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
11.4 Torque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
11.5 Moment of inertia . . . . . . . . . . . . . . . . . . . . . . . . 167
11.6 Unrolling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
11.7 Simulating a yo-yo . . . . . . . . . . . . . . . . . . . . . . . 173
Index 177
x CONTENTS
P
reface
Thi
sbooki
sabou
tm od
el
ingands
imula
tionofph
ysi
calsy
stem
s. Th
efo
llow
ing
di
agramshow
swh a
tI me
anb y“mod
el
in g
”:
Pr
l
Ana
lys
is
Mode
edi
ct
S
imu
lat
ion
i
on
ion
Val
ct
idat
stra
ion
Ab
tem
Dat
ys
a
S
Mea
suremen
t
St
artinginthelowe
rl e
ft
,thesystemissometh
inginth
erea
lwor
ldweare
int
erestedin
.O f
ten,i
tissome
thingcomp
li
cated
,sowehavet
ode
cidewh
ich
de
tai
l scanbesimpl
ifiedorabs
tractedaway
.
There
sultofabst
r a
cti
onisamode l thatinc
lud
esthefeatu
resweth
inkare
es
sent
ial
.Am odelcanberepr
esen
tedintheformofdi
agramsandequa
tion
s,
wh
ichcanb eusedform a
themat
ica
lana lys
is.Itcanal
sob eimp
lemen
tedin
thef
ormo facompu terpro
gram,whi
chc anruns
imulations.
Th
ere
sul
tofan
aly
sisands
imu
lat
ionc
anb
ea p
red
ict
ionabou
t wh
atth
e
xii Chapter 0 Preface
system will do, an explanation of why it behaves the way it does, or a design
intended to achieve a purpose.
This process is almost always iterative: 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 asked you why the orbit of the Earth is nearly
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.
So 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.
0.1 Can modeling be taught? xiii
At Olin College, we use this book in a class called Modeling and Simulation,
which all students take in their first semester. My colleagues, John Geddes
and Mark Somerville, and I developed this class and taught it for the first time
in 2009.
It is based on our belief that modeling should be taught explicitly, early, and
throughout the curriculum. It is also based on our conviction that computation
is an essential part of this process.
If students are limited to the mathematical analysis they can do by hand, they
are restricted to a small number of simple physical systems, like a projectile
moving in a vacuum or a block on a frictionless plane.
And they will only work with bad models; that is, models that are too simple
for their purpose. In nearly every mechanical system, air resistance and friction
are essential features; if we ignore them, our predictions will be wrong and our
designs won’t work.
If you have programmed before, you will have an easier time getting started,
but you might be uncomfortable in some places. I take an approach to pro-
gramming you have probably not seen before.
1. They go “bottom up”, starting with basic language features and gradu-
ally adding more powerful tools. As a result, it takes a long time before
students can do anything more interesting than convert Fahrenheit to
Celsius.
In this book, you learn to program with an immediate goal in mind: writing
simulations of physical systems. And we proceed “top down”, by which I mean
we use professional-strength data structures and language features right away.
In particular, we use the following Python libraries:
These tools let you work on more interesting programs sooner, but there are
some drawbacks: they can be hard to use, and it can be challenging to keep
track of which library does what and how they interact.
Some features in the modsim library are like training wheels; at some point you
will probably stop using them and start working with the underlying libraries
directly. Other features you might find useful the whole time you are working
through the book, or even later.
I encourage you to read the the modsim library code. Most of it is not com-
plicated, and I tried to make it readable. Particularly if you have some pro-
gramming experience, you might learn something by reverse-engineering my
designs.
More importantly you should understand what those concepts mean; but if
you don’t, this book might help you figure it out.
As for science, we will cover topics from a variety of fields, including demogra-
phy, epidemiology, medicine, thermodynamics, and mechanics. For the most
part, I don’t assume you know anything about these topics. In fact, one of the
2
And if you noticed that those two questions answer each other, even better.
xvi Chapter 0 Preface
skills you need to do modeling is the ability to learn enough about new fields
to develop models and simulations.
I think that’s everything you need, but if you find that I left something out,
please let me know.
1. Install Python on your computer, along with the libraries we will use.
3. Run Jupyter, which is a tool for running and writing programs, and load
a notebook, which is a file that contains code and text.
The next three sections provide details for these steps. I wish there were an
easier way to get started; it’s regrettable that you have to do so much work
before you write your first program. Be persistent!
You could update Python and install these libraries, but I strongly recom-
mend that you don’t go down that road. I think you will find it easier to use
0.6 Copying my files xvii
Anaconda, which is a free Python distribution that includes all the libraries
you need for this book (and lots more).
Anaconda is available for Linux, macOS, and Windows. By default, it puts all
files in your home directory, so you don’t need administrator (root) permission
to install it, and if you have a version of Python already, Anaconda will not
remove or modify it.
There are several ways you can copy the files from my repository to your
computer.
If you don’t want to use Git at all, you can download my files in a Zip archive
from http://modsimpy.com/zip. Then you need a program like WinZip or
gzip to unpack the Zip file.
To use Git, you need a Git client, which is a program that manages git repos-
itories. If you have not used Git before, I recommend GitHub Desktop, which
is a simple graphical Git client. You can download it from https://desktop.
github.com. Currently, GitHub Desktop is not available for Linux. On Linux,
I suggest using the Git command-line client. Installation instructions are at
http://modsimpy.com/git.
Once you have a Git client, you can use it to copy files from my repository to
your computer, which is called cloning in Git’s vocabulary. If you are using
a Command-line git client, type
You don’t need a GitHub account to do this, but you won’t be able to write
your changes back to GitHub.
If you want to use GitHub to keep track of the code you write while you are
using this book, you can make of a copy of my repository on GitHub, which
is called forking. If you don’t already have a GitHub account, you’ll need to
create one.
Contributor List
If you have a suggestion or correction, send it to [email protected].
Or if you are a Git use, send me a pull request!
If I make a change based on your feedback, I will add you to the contributor
list, unless you ask to be omitted.
If you include at least part of the sentence the error appears in, that makes it
easy for me to search. Page and section numbers are fine, too, but not as easy
to work with. Thanks!
0.7 Running Jupyter xix
I am grateful to John Geddes and Mark Somerville for their early col-
laboration with me to create Modeling at Simulation, the class at Olin
College this book is based on.
Modeling
The world is a complicated place. In order to make sense of it, we use models,
which are generally smaller and simpler than the thing we want to study. The
word “model” means different things in different contexts, so it is hard to
define except by example.
Some models are actual objects, like a scale model of a car, which has the
same shape as the car, but smaller. Scale models are often useful for testing
properties of mechanical systems, like air resistance.
This book is about mathematical models, which are ideas, not objects. If
you studied Newton’s laws of motion, what you learned is a mathematical
model of how objects move in space when forces are applied to them.
We can test this myth by making and analyzing a model. To get started, I’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
2 Chapter 1 Modeling
acting on the penny is gravity, which causes the penny to accelerate downward.
If the initial velocity is 0, the velocity after t seconds is at, and the height the
penny has dropped at t is
h = at2 /2
Using algebra, we can solve for t:
p
t= 2h/a
Plugging in the acceleration of gravity, a = 9.8 m/s2 and the height of the
Empire State Building, h = 381 m, we get t = 8.8 s. Then computing v = at
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 simplifi-
cations. For example, we assume that gravity is constant. In fact, the force
of gravity is different on different parts of the globe, and 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 18 m/s, the upward force of air resistance equals the downward
force of gravity, so the penny stops accelerating. After that, it doesn’t matter
how far the penny falls; it hits the sidewalk (or your head) at about 18 m/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 is also wrong, but much
better, and probably 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 http://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.
1.2 Computation 3
1.2 Computation
There are (at least) two ways to work with mathematical models, analysis
and simulation. Analysis often involves algebra and other kinds of symbolic
manipulation. Simulation often involves computers.
In this book we do some analysis and a lot of simulation; along the way, I
discuss the pros and cons of each. The primary tools we use for simulation are
the Python programming language and Jupyter, which is an environment for
writing and running programs.
As a first example, I’ll show you how I computed the results from the previous
section using Python. You can view this example, and the other code in this
chapter, at http://modsimpy.com/chap01. For instructions for downloading
and running the code, see Section 0.4.
Operation Symbol
Addition +
Subtraction -
Multiplication *
Division /
Exponentiation **
Next, we can compute the time it takes for the penny to drop 381 m, the height
of the Empire State Building.
h = 381 * meter
t = sqrt(2 * h / a)
4 Chapter 1 Modeling
These lines create two more variables: h gets the height of the building in
meters; t gets the time, in seconds, for the penny to fall to the sidewalk. sqrt
is a function that computes square roots. Python keeps track of units, so the
result, t, has the correct units, seconds.
v = a * t
This example demonstrates the features of Python we’ll use to develop com-
putational simulations of real-world systems. Along the way, I will make deci-
sions about how to model the system. In the next chapter we’ll review these
decisions.
Suppose the system contains 12 bikes and two bike racks, one at Olin and one
at Wellesley, each with the capacity to hold 12 bikes.
As students arrive, check out a bike, and ride to the other campus, the number
of bikes in each location changes. In the simulation, we’ll need to keep track of
where the bikes are. To do that, I’ll create a System object, which is defined
in the modsim library.
This line of code is an import statement that tells Python to read the file
modsim.py and make the functions it defines available.
1.3 Modeling a bike share system 5
Functions in the modsim.py library include sqrt, which we used in the previous
section, and System, which we are using now. System creates a System object,
which is a collection of system variables.
In this example, the system variables are olin and wellesley and they repre-
sent the number of bikes at Olin and Wellesley. The initial values are 10 and
2, indicating that there are 10 bikes at Olin and 2 at Wellesley. The System
object created by System is assigned to a new variable named bikeshare.
We can read the variables inside a System object using the dot operator,
like this:
bikeshare.olin
bikeshare.wellesley
The result is 2. If you forget what variables a system object has, you can just
type the name:
bikeshare
The result looks like a table with the variable names and their values:
value
olin 10
wellesley 2
The system variables and their values make up the state of the system. We
can update the state by assigning new values to the variables. For example,
if a student moves a bike from Olin to Wellesley, we can figure out the new
values and assign them:
bikeshare.olin = 9
bikeshare.wellesley = 3
6 Chapter 1 Modeling
bikeshare.olin -= 1
bikeshare.wellesley += 1
1.4 Plotting
As the state of the system changes, it is often useful to plot the values of the
variables over time. The modsim library provides a functions that creates a
new figure:
newfig()
These commands are not actually Python; they are so-called “magic com-
mands” that control the behavior of Jupyter.
plot(bikeshare.olin, 'rs-')
plot(bikeshare.wellesley, 'bo-')
The first argument is the variable to plot. In this example, it’s a number,
but we’ll see later that plot can handle other objects, too.
The second argument is a “style string” that determines what the plot
should look like. In general, a string is a sequence of letters, numbers,
and punctuation that appear in quotation marks. The style string 'rs-'
means we want red squares with lines between them; 'bo-' means we
want blue circles with lines.
The plotting functions in the modsim library are based on Matplotlib, which is
a Python library for generating figures. To learn more about these functions,
you can read the Matplotlib documentation. For more about style strings, see
http://modsimpy.com/plot.
When you are developing code in Jupyter, it is often efficient to write 1–2 lines
in each cell, test them to confirm they do what you intend, and then use them
to define a new function. For example, these lines move a bike from Olin to
Wellesley:
bikeshare.olin -= 1
bikeshare.wellesley += 1
Rather than repeat them every time a bike moves, we can define a new func-
tion:
def bike_to_wellesley():
bikeshare.olin -= 1
bikeshare.wellesley += 1
def is a special word in Python that indicates we are defining a new function.
The name of the function is bike_to_wellesley. The empty parentheses indi-
cate that this function takes no arguments. The colon indicates the beginning
of an indented code block.
8 Chapter 1 Modeling
The next two lines are the body of the function. They have to be indented;
by convention, the indentation is 4 spaces.
When you define a function, it has no immediate effect. The body of the func-
tion doesn’t run until you call the function. Here’s how to call this function:
bike_to_wellesley()
When you call this function, it updates the variables of the bikeshare object;
you can check by displaying or plotting the new state.
When you call a function that takes no arguments, you have to include the
empty parentheses. If you leave them out, like this:
bike_to_wellesley
<function __main__.bike_to_wellesley>
1.6 Parameters
Similarly, we can define a function that moves a bike from Wellesley to Olin:
def bike_to_olin():
bikeshare.wellesley -= 1
bikeshare.olin += 1
bike_to_olin()
1.6 Parameters 9
One benefit of defining functions is that you avoid repeating chunks of code,
which makes programs smaller. Another benefit is that the name you give the
function documents what it does, which makes programs more readable.
In this example, there is one other benefit that might be even more important.
Putting these lines in a function makes the program more reliable because it
guarantees that when we decrease the number of bikes at Olin, we increase
the number of bikes at Wellesley. That way, we guarantee that the bikes in
the model are neither created nor destroyed!
However, now we have two functions that are nearly identical except for a
change of sign. Repeated code makes programs harder to work with, because
if we make a change, we have to make it in several places.
We can avoid that by defining a more general function that moves any number
of bikes in either direction:
def move_bike(n):
bikeshare.olin -= n
bikeshare.wellesley += n
move_bike(1)
It assigns the value of the argument, 1, to the parameter, n, and then runs the
body of the function. So the effect is the same as:
n = 1
bikeshare.olin -= n
bikeshare.wellesley += n
move_bike(-1)
10 Chapter 1 Modeling
n = -1
bikeshare.olin -= n
bikeshare.wellesley += n
Which moves a bike from Wellesley to Olin. Now that we have move_bike, we
can rewrite the other two functions to use it:
def bike_to_wellesley():
move_bike(1)
def bike_to_olin():
move_bike(-1)
If you define the same function name more than once, the new definition
replaces the old one.
Normally when Jupyter runs the code in a cell, it displays the value of the last
line of code. For example, if you run:
bikeshare.olin
bikeshare.wellesley
Jupyter runs both lines of code, but it only displays the value of the second
line.
If you want to display more than one value, you can use print statements:
print(bikeshare.olin)
print(bikeshare.wellesley)
1.8 If statements 11
print(bikeshare.olin, bikeshare.wellesley)
In this example, the two values appear on the same line, separated by a space.
Print statements are also useful for debugging functions. For example, if you
add a print statement to move_bike, like this:
def move_bike(n):
print('Running move_bike with n =', n)
bikeshare.olin -= n
bikeshare.wellesley += n
The first argument of print is a string; the second is the value of n, which is
a number. Each time you run move_bike, it displays a message and the value
n.
1.8 If statements
The modsim library provides a function called flip that takes as an argument
a probability between 0 and 1:
flip(0.7)
The result is one of two values: True with probability 0.7 or False with
probability 0.3. If you run this function 100 times, you should to get True
about 70 times and False about 30 times. But the results are random, so
they might differ from these expectations.
True and False are special values defined by Python. Note that they are
not strings. There is a difference between True, which is a special value, and
'True', which is a string.
True and False are called boolean values because they are related to Boolean
algebra (http://modsimpy.com/boolean).
12 Chapter 1 Modeling
We can use boolean values to control the behavior of the program using an if
statement:
if flip(0.5):
print('heads')
If the result from flip is True, the program displays the string 'heads'.
Otherwise it does nothing.
Optionally, you can add an else clause to indicate what should happen if the
result is False:
if flip(0.5):
print('heads')
else:
print('tails')
Now we can use flip to simulate the arrival of students who want to borrow
a bike. Suppose we have data from previous observations about how many
students arrive at a particular time of day. If students arrive every 2 minutes,
on average, then during any one-minute period, there is a 50% chance a student
will arrive:
if flip(0.5):
bike_to_wellesley()
if flip(0.5):
bike_to_olin()
We can combine these snippets of code into a function that simulates a time
step, which is an interval of time, like one minute:
1.8 If statements 13
def step():
if flip(0.5):
bike_to_wellesley()
if flip(0.5):
bike_to_olin()
Then we can run a time step, and update the plot, like this:
step()
plot_state()
In reality, the probability of an arrival will vary over the course of a day, and
might be higher or lower, at any point in time, at Olin or Wellesley. So instead
of putting the constant value 0.5 in step we can replace it with a parameter,
like this:
def step(p1, p2):
if flip(p1):
bike_to_wellesley()
if flip(p2):
bike_to_olin()
Now when you call step, you have to provide two arguments:
step(0.4, 0.2)
The arguments you provide, 0.4 and 0.2, get assigned to the parameters, p1
and p2, in order. So running this function has the same effect as:
p1 = 0.4
p2 = 0.2
if flip(p1):
bike_to_wellesley()
if flip(p2):
bike_to_olin()
14 Chapter 1 Modeling
if flip(p2):
bike_to_olin()
Because they have default values, these parameters are optional; if you run
step with no arguments, and don’t forget the parentheses, like this:
step()
The parameters get the default values, so p1 and p2 are both 0.5. If you
provide one argument, like this:
step(0.4)
The value you provide overrides the default value of p1, but p2 still gets the
default. If you provide two arguments, it overrides both.
step(0.4, 0.2)
If you want to override p2 only, and accept the default for p1, you have to
provide the name of the parameter explicitly:
step(p2=0.2)
step(p1=0.4, p2=0.2)
Providing parameters names makes programs more readable and less error-
prone, because you don’t have to worry about the order of the arguments.
1.10 For loops 15
For example, if you reverse the order, it still assigns the right value to each
parameter:
step(p2=0.2, p1=0.4)
for i in range(4):
bike_to_wellesley()
plot_state()
The punctuation here should look familiar; the first line ends with a colon,
and the lines inside the for loop are indented. The other elements of the for
loop are:
The words for and in are special words we have to use in a for loop.
i is a loop variable that gets created when the for loop runs. In this
example we don’t actually use i; we will see examples later where we
use the loop variable inside the loop.
When this loop runs, it runs the statements inside the loop four times, which
moves one bike at a time from Olin to Wellesley, and plots the updated state
of the system after each move.
1.11 Debugging
The goal of this chapter is to give you the minimal set of tools to get you
started. At this point, you know enough to write simple simulations of systems
16 Chapter 1 Modeling
like the Olin–Wellesley bikeshare. Along with each chapter, I provide a Jupyter
notebook that contains the code from the chapter, so you can run it, see how
it works, modify it, and see how it breaks.
When something goes wrong, Python provides error messages with information
about the problem. This information can help with debugging, but error
messages are often hard to understand.
Simulation
To paraphrase two Georges, “All models are wrong, but some models are more
wrong than others.” In this chapter, I demonstrate the process we use to make
models less wrong.
As an example, we’ll review the bikeshare model from the previous chapter,
consider its strengths and weaknesses, and gradually improve it. We’ll also see
ways to use the model to understand the behavior of the system and evaluate
designed intended to make it work better.
You can view the code for this chapter at http://modsimpy.com/chap02. For
instructions for downloading and running the code, see Section 0.4.
The model does not account for travel time from one bike station to
another.
The model does not check whether a bike is available, so it’s possible for
the number of bikes to be negative (as you might have noticed in some
of your simulations).
Some of these modeling decisions are better than others. For example, the first
assumption might be reasonable if we simulate the system for a short period
of time, like one hour.
The second assumption is not very realistic, but it might not affect the results
very much, depending on what we use the model for.
On the other hand, the third assumption seems problematic, and it is relatively
easy to fix. In Section 2.4, we will.
This process, starting with a simple model, identifying the most important
problems, and making gradual improvements, is called iterative modeling.
For any physical system, there are many possible models, based on different
assumptions and simplifications. It often takes several iterations to develop a
model that is good enough for the intended purpose, but no more complicated
than necessary.
Here are two functions from the previous chapter, move_bike and plot_state:
2.2 More than one System object 19
def move_bike(n):
bikeshare.olin -= n
bikeshare.wellesley += n
def plot_state():
plot(bikeshare.olin, 'rs-', label='Olin')
plot(bikeshare.wellesley, 'bo-', label='Wellesley')
One problem with these functions is that they always use bikeshare, which
is a System object. As long as there is only one System object, that’s fine,
but these functions would be more flexible if they took a System object as a
parameter. Here’s what that looks like:
def plot_state(system):
plot(system.olin, 'rs-', label='Olin')
plot(system.wellesley, 'bo-', label='Wellesley')
bike_to_olin(bikeshare1)
bike_to_wellesley(bikeshare2)
Changes in bikeshare1 do not affect bikeshare2, and vice versa. This be-
havior will be useful later in the chapter when we create a series of System
objects to simulate different scenarios.
20 Chapter 2 Simulation
2.3 Documentation
Another problem with the code we have so far is that it contains no documen-
tation. Documentation is text we add to programs to help other programmers
read and understand them. It has no effect on the program when it runs.
The first line is a single sentence that describes what the function does.
At this point we have more documentation than code, which is not unusual
for short functions.
The changes we’ve made so far improve the quality of the code, but we haven’t
done anything to improve the quality of the model yet. Let’s do that now.
Currently the code does not check whether a bike is available when a customer
arrives, so the number of bikes at a location can be negative. That’s not very
realistic. Here’s an updated version of move_bike that fixes the problem:
22 Chapter 2 Simulation
wellesley_temp = system.wellesley + n
if wellesley_temp < 0:
return
The comments explain the two sections of the function: the first checks to
make sure a bike is available; the second updates the state.
The first line creates a variable named olin_temp that gets the number of
bikes that would be at Olin if n bikes were moved. I added the suffix _temp to
the name to indicate that I am using it as a temporary variable.
In that case I use a return statement, which causes the function to end
immediately, without running the rest of the statements. So if there are not
enough bikes at Olin, we “return” from move_bike without changing the state.
We do the same if there are not enough bikes at Wellesley.
If both of these tests pass, we run the last two lines, which assigns the values
from the temporary variables to the corresponding system variables.
In contrast, the system variables olin and wellesley belong to system, the
System object that was passed to this function as a parameter. When we
2.5 Comparison operators 23
change system variables inside a function, those changes are visible in other
parts of the program.
This version of move_bike makes sure we never have negative bikes at either
station. But what about bike_to_wellesley and bike_to_olin; do we have
to update them, too? Here they are:
def bike_to_wellesley(system):
move_bike(system, 1)
def bike_to_olin(system):
move_bike(system, -1)
Because these functions use move_bike, they take advantage of the new feature
automatically. We don’t have to update them.
Operation Symbol
Less than <
Greater than >
Less than or equal <=
Greater than or equal >=
Equal ==
Not equal !=
The equals operator, ==, compares two values and returns True if they are
equal and False otherwise. It is easy to confuse with the assignment op-
erator, =, which assigns a value to a variable. For example, the following
statement uses the assignment operator, which creates x if it doesn’t already
exist and gives it the value 5
24 Chapter 2 Simulation
x = 5
On the other hand, the following statement checks whether x is 5 and returns
True or False. It does not create x or change its value.
x == 5
if x == 5:
print('yes, x is 5')
If you make a mistake and use = in the first line of an if statement, like this:
if x = 5:
print('yes, x is 5')
That’s a syntax error, which means that the structure of the program is
invalid. Python will print an error message and the program won’t run.
2.6 Metrics
Getting back to the bike share system, at this point we have the ability to
simulate the behavior of the system. Since the arrival of customers is random,
the state of the system is different each time we run a simulation. Models like
this are called stochastic; models that do the same thing every time they run
are deterministic.
Suppose we want to use our model to predict how well the bike share system
will work, or to design a system that works better. First, we have to decide
what we mean by “how well” and “better”.
From the customer’s point of view, we might like to know the probability of
finding an available bike. From the system-owner’s point of view, we might
want to minimize the number of customers who don’t get a bike when they
want one, or maximize the number of bikes in use. Statistics like these that
are intended to quantify how well the system works are called metrics.
2.6 Metrics 25
wellesley_temp = system.wellesley + n
if wellesley_temp < 0:
system.wellesley_empty += 1
return
system.olin = olin_temp
system.wellesley = wellesley_temp
print(bikeshare.olin_empty, bikeshare.wellesley_empty)
Because the simulation is stochastic, the results are different each time it runs.
26 Chapter 2 Simulation
t = sqrt(2 * h / a)
Not all functions have return values. For example, when you run plot, it adds
a point to the current graph, but it doesn’t return a value. The functions we
wrote so far are like plot; they have an effect, like changing the state of the
system, but they don’t return values.
To write functions that return values, we can use a second form of the return
statement, like this:
def add_five(x):
return x + 5
add_five(3)
As a more useful example, here’s a new function that creates a System object,
runs a simulation, and then returns the System object as a result:
def run_simulation():
system = System(olin=10, wellesley=2,
olin_empty=0, wellesley_empty=0)
run_steps(system, 60, 0.4, 0.2, plot=False)
return system
system = run_simulation()
It assigns to system a new System object, which we can use to check the
metrics we are interested in:
print(system.olin_empty, system.wellesley_empty)
This version of run_simulation always starts with the same initial condition,
10 bikes at Olin and 2 bikes at Wellesley, and the same values of p1, p2, and
num_steps. Taken together, these five values are the parameters of the
model, which are values that determine the behavior of the model.
Then we can see how the metrics, like the number of unhappy customers,
depend on the parameters of the model. But before we do that, we need a
new version of a for loop.
28 Chapter 2 Simulation
for i in range(4):
bike_to_wellesley()
plot_state()
The loop variable, i, gets created when the loop runs, but it is not used for
anything. Now here’s a loop that actually uses the loop variable:
for p1 in p1_array:
print(p1)
0.0
0.25
0.5
0.75
1.0
ebookbell.com