Professional Documents
Culture Documents
December 3, 2021
The present text was inspired by and is a derivative of the text: “Physical Modeling in MATLAB® ”, version
1.1.8, by Allen B. Downey. The latest version of the book is available at http://greenteapress.com/wp/
physical-modeling-in-matlab/ and is published by Green Tea Press.
Permission is granted to copy, distribute, and/or modify this document under the terms of the Creative
Commons Attribution-NonCommercial 3.0 Unported License, which is available at http://creativecommons.
org/licenses/by-nc/3.0/. This is the license under which “Physical Modeling in MATLAB® ” was originally
published.
Preface
When I joined the faculty of the Department of Chemical, Paper and Biomedical
Engineering at Miami University in Fall 2013, I was tasked with developing a com-
putational methods course for our chemical and bio- engineering majors. I was
encouraged to design the course around the use of MATLAB® ; our bioengineering
students take a sequence of courses in electrical engineering which extensively
use MATLAB® , and a comparable course taught in mechanical and manufacturing
engineering also uses MATLAB® . The course created was CPB 324 Chemical and
Bio- Engineering Computation and Statistics. With the time required for the new
course to be approved by the university, and then to become a required course
in our chemical and bio- engineering curriculum (which also requires university
approval), I did not teach my first section of the course until Spring 2017.
During my time at Miami University I have taught a total of 6 core chemical
courses: fluid mechanics, mass transfer, thermodynamics 1 and 2, reactor design,
and chemical engineering process design. I also work alongside undergraduate
students in my computational thermodynamics laboratory, have attended work-
shops on e-learning and “open” textbooks, and taught a class at Universidade
Federal Fluminense (UFF), in Niterói, Rio de Janerio, Brazil. All of these expe-
riences have shaped the current version of this course, which differs from my
original vision. Every semester I teach the course I continue to modify the content
in an attempt to optimize student success. Beginning with the first offering of this
course, I have kept four major concepts in mind:
1. Assume that the students in the class have never programmed before, and
have no previous experience with MATLAB® . While students at Miami
University are introduced to MATLAB® during the their first year in CPB 102
Introduction to Chemical and Bio- Engineering, the introduction is brief
and students are too often overwhelmed with their first year studies such
that it is as if the encounter never happened.
iii
iv
3. To create an “open” (free) textbook for the class. This format allows me to:
lower the barrier of education, allowing anyone and everyone to obtain
a copy; completely customize the text for class; and to create a resource
for future classes and future endeavors. Our students are asked to learn a
tremendous amount of material in a short period of time. While a year after
completing the course I can not expect them to remember everything, what
is important is they know where to turn for help. They will always have
access to the latest version of this text, which I plan to grow and improve
every semester.
4. And lastly, to teach the course material so that students are encouraged
to use MATLAB® in their future course studies. Too often engineers are
associated with making assumptions in order to solve problems. While this
may have been necessary fifty years ago, it need not necessarily be the case
today. With computational tools we may solve problems that we would
never imagine solving otherwise. Additionally, as a student, mathematical
modeling with MATLAB® can be an excellent learning tool. Rather than
just reading about a theory in a text, we can set-up a physical modeling,
and then readily perform virtual experiments; we can change parameters
and look at what the effect of each is. To encourage this, we will solve
problems in the course from upper level classes, where you need not know
the underlying theory, but can solve the problems using MATLAB® and can
further use MATLAB® to gain insight.
As mentioned, the course is designed assuming that the students in class have
never programmed before. This is different from most texts that use MATLAB® ,
which are aimed at readers who know how to program. As a result, the order of
presentation is unusual. The text starts with scalar values and works up to vectors
and matrices very gradually. This approach is good for beginning programmers,
because it is hard to understand composite objects until you understand basic
programming semantics. But there are problems:
• Many of the examples in the first few chapters are not idiomatic MATLAB® .
This problem is addressed in the later chapters by translating the examples
into a more standard style.
v
The texts puts a lot of emphasis on functions, in part because they are an
important mechanism for controlling program complexity, and also because
they are useful for working with MATLAB® tools; we will look at fzero, fsolve,
ode45, and integral in this text. However, they are additionally central to the
use of many other tools students will likely encounter in their future studies; for
example, fminbnd comes up in my thermodynamics course, and after completing
this course I find students are readily able to use it after consulting the excellent
documentation provided by MATLAB® .
In teaching the course I have to be selective of the material covered. Typically,
the first third of the course can be thought of as an introduction to computer
programming. While we will use MATLAB® , the logic is equally applicable to other
languages and programs. I find that students struggle most with this material, so
we spend ample time working through examples. In last two thirds of the course
we then turn and look at some of MATLAB’s® built-in functionalities. We begin
with root-finding and systems of equations, and then work our way up to systems
of ordinary differential equations. These are skills I wish my class had when I
taught our course in reactor design. The text then moves to interpolation, which
results naturally as a complement to numerically solving ordinary differential
equations. We then end with numerical integration. At present, this is all that I
can comfortably cover in a semester. However, recently this text has been adopted
by CPB 102 Introduction to Chemical and Bio- Engineering which teaches the first
few chapters. I hope that in the near future this will allow me to add additional
material on optimization, curve fitting, and symbolic calculations.
When a new numerical method is introduced, a brief amount of time will be
spent writing and using our own function. This text is not a text on numerical
methods. We will look at some basic numerical methods to help understand
how MATLAB’s® built-in functions work. While this is good to help students
appreciate MATLAB® , I find it invaluable to better understand the required inputs,
the outputs, and what can go wrong. Links will be provided to external resources
(typically Wikipedia and textbooks available in the Open Textbook Library) for
the reader interested in learning more.
While the text is formatted to be printed, I believe the electronic copy is
much better. You will notice that hyperlinking is used throughout the text. First,
the documentation provided by MATLAB® is invaluable. You will find that I link
to it throughout the text. I emphasize the documentation is invaluable, and I
encourage my students to consult it throughout the text. This not only improves
student learning of the topics covered in the text, but it helps students become
comfortable using the documentation in the future if they need to learn a new
feature of MATLAB® . Second, in the text I provide copies of all of my M-files.
Additionally, you will find that in the M-file caption, I link to a digital copy stored
in my Google Drive account that you may download. Third, links are provided
throughout to screen casts on my YouTube channel. I would encourage everyone
using this text to subscribe. Initially, my screen casts will be of solutions to select
examples and topics that frequently cause confusion, but I will continue to grow
vi
the channel and improve the quality of the screen casts. You may notice my
solutions in the screen casts differ slightly from the text; in the screen casts I
am genuinely solving the problems and offer commentary throughout. When
applicable, the description of the YouTube video contains a link the my Google
Drive account to download the M-file created in the video. At present I have
not updated the text solutions to be identical to the screen casts to provide an
additional example.
At present, the text is only available as a PDF document. I have spent a great
amount of time working on an HTML version. However, in the end, I believe the
PDF is better. It allows me to readily share the text with anyone via a single email.
And I believe the use of hyperlinking allows the text to come alive, similar to what
the user experience with the HTML version might be. Before getting started have
a look at my screen cast demonstrating the use of hyperlinking.
Additionally in the spirit of free and accessible, I plan to evolve the text from
exclusively using MATLAB® to also use freely available software. I will start with
GNU Octave, which is what I actually use in my research group. At present, GNU
Octave can be used throughout most of the text. The exception is I present the
option of using nested functions, which GNU Octave does not support. This
appears later in the text when solving systems of second order initial value ordi-
nary differential equations. I delay the introduction of nested functions because
they often cause confusion initially because I frequently repeat the message the
functions have their own workspace. However, for the trajectory problems we
solve, the use of nested functions greatly simplifies our programs as compared to
attempting to use anonymous functions.
If you have any suggestions or comments, please let me know. If you are
interested in helping to develop the text and/or the YouTube channel, I would
love to hear from you. If you encounter a problem in another course you would
like to solve using MATLAB® let me know and material can be added to the text.
Please also let me know if you would like to contribute the material to the text.
Please also know that many excellent (and freely available) resources are
available to you to help you learn MATLAB® . You should bookmark the online
documentation. MathWorks® , the fine folks that develop MATLAB® , also have
an “Academia” section on their website worth checking out. You will find many
excellent MATLAB® tutorials available. You will also find that many other resources
are available. For example, a free “Introduction to Programming with MATLAB”
course is offered by Vanderbilt University on coursera. The course covers material
from the first third of this course, and a series of very good YouTube videos are
available. Many others exist, this is just an example.
Lastly, I would like to acknowledge and thank Professor Allen B. Downey,
Professor of Computer Science at Olin College, who has written many “open”
textbooks. This text was inspired by and is a derivative of his textbook “Physical
Modeling in MATLAB® ”, version 1.1.8, which is published under the terms of the
Creative Commons Attribution-NonCommercial 3.0 Unported License. The text
is additionally available in the Open Textbook Library, which I would encourage
vii
you to visit.
Contents
2 Scripts 23
2.1 M-files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.2 Why scripts? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.3 The workspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.4 More errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.5 Pre- and post-conditions . . . . . . . . . . . . . . . . . . . . . . . . 29
2.6 Assignment and equality . . . . . . . . . . . . . . . . . . . . . . . . 30
2.7 Updating variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.8 Incremental development . . . . . . . . . . . . . . . . . . . . . . . 35
2.9 Unit testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.10 Kinds of error . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.11 Absolute and relative error . . . . . . . . . . . . . . . . . . . . . . . 38
2.12 CPB Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.13 Glossary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.14 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
ix
x CONTENTS
3.4 Series . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.5 Generalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
3.6 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.7 CPB Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
3.8 Creating multiple figures . . . . . . . . . . . . . . . . . . . . . . . . 73
3.9 Glossary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
3.10 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5 Functions 135
5.1 Name Collisions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
5.2 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
5.3 Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
5.4 Function names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
5.5 Multiple input variables . . . . . . . . . . . . . . . . . . . . . . . . 143
5.6 Logical functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
5.7 An incremental development example . . . . . . . . . . . . . . . . 149
5.8 Nested loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
5.9 Conditions and flags . . . . . . . . . . . . . . . . . . . . . . . . . . 151
5.10 Encapsulation and generalization . . . . . . . . . . . . . . . . . . . 153
5.11 A misstep . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
CONTENTS xi
7 Zero-finding 203
7.1 Why functions? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
7.2 Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
7.3 A note on notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
7.4 Nonlinear equations . . . . . . . . . . . . . . . . . . . . . . . . . . 205
7.5 Zero-finding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
7.6 fzero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
7.7 What could go wrong? . . . . . . . . . . . . . . . . . . . . . . . . . . 209
7.8 Finding an initial guess . . . . . . . . . . . . . . . . . . . . . . . . . 211
7.9 More name collisions . . . . . . . . . . . . . . . . . . . . . . . . . . 214
7.10 Debugging in four acts . . . . . . . . . . . . . . . . . . . . . . . . . 216
7.11 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
7.12 Functions: what we’ve done so far . . . . . . . . . . . . . . . . . . 221
7.13 Anonymous function . . . . . . . . . . . . . . . . . . . . . . . . . . 222
7.14 What could go wrong? . . . . . . . . . . . . . . . . . . . . . . . . . . 225
7.15 Saving anonymous functions . . . . . . . . . . . . . . . . . . . . . 226
7.16 Revisiting the duck . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
7.17 Numerical methods: zero-finding . . . . . . . . . . . . . . . . . . . 229
7.17.1 The bisection method . . . . . . . . . . . . . . . . . . . . . 229
7.17.2 The secant (or linear interpolation) method . . . . . . . . 237
7.18 roots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
7.19 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
7.20 CPB Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
xii CONTENTS
12 Interpolation 443
12.1 Discrete and continuous maps . . . . . . . . . . . . . . . . . . . . 443
12.2 Interpolation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444
12.3 Interpolating the inverse function . . . . . . . . . . . . . . . . . . 448
12.4 Creating a function . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
12.5 Field mice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453
12.6 Good results come from good users . . . . . . . . . . . . . . . . . . 457
12.7 Fun with tabulated pure component VLE data (i.e., the saturated
steam tables) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466
12.7.1 Downloading the saturated steam tables . . . . . . . . . . 466
12.7.2 dlmread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
12.7.3 interp1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
12.7.4 Plotting phase diagrams . . . . . . . . . . . . . . . . . . . . 476
12.8 Glossary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480
12.9 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
In Chapters 1 to 6 we begin with some MATLAB “basics.” The goal is the start by t You may see different win-
building a strong foundational knowledge. From there, the MATLAB world is your dows and/or a different win-
dow layout upon starting
oyster. By the end of this chapter you will be able to:
MATLAB. The layout may
• Demonstrate the ability to perform basic mathematical operations in the be modified using the large
Layout icon on the Home
MATLAB command window
menu tab. You can also click
• Recall how to assign variables, order of operations, and how to search the on the windows and drag
them to re-arrange.
documentation
1
2 Chapter 1 Variables and values
Just to be clear: in the example above, MATLAB printed »; I typed 2 + 1 and then
hit Enter , and MATLAB printed ans = 3. And when I say “printed,” I really mean
“displayed on the screen,” which might be confusing, but it’s the way MATLAB folk
talk (or computer scientists in general).
An expression can contain any number of operators and operands. You don’t
have to put spaces between them; some people do and some people don’t.
>> 1+2+3+4+5+6+7+8+9
1.1 A glorified calculator 3
ans = 45
Speaking of spaces, you might have noticed that MATLAB puts some space be-
tween the expression and ans =, and between ans = and the result. In my exam-
ples I will leave it out to save paper.
The other arithmetic operators are pretty much what you would expect. Sub-
traction is denoted by a minus sign, –; multiplication by an asterisk, * (sometimes
pronounced “splat”); division by a forward slash /.
The order of operations is what you would expect from basic algebra: paren- t Use parentheses, do NOT
theses, exponents, multiplication and division, and addition and subtraction. use brackets. Brackets
In the United States the acronym “PEMDAS” or the mnemonic phrase “Please mean something differ-
Excuse My Dear Aunt Sally” is commonly used to recall the order of operations. ent to MATLAB, and will be
Exponentiation happens before multiplication and division, followed by addition used later when working
with vectors and matrices.
and subtraction; if you want to override the order of operations, you can use
parentheses.
t When in doubt of the order
of operations, add paren-
>> 2 * (3-4) / 5 theses. This includes paren-
ans = -0.4000 theses around exponents.
I sometimes do this out of
When I added the parentheses I also changed the spacing to make the grouping of habit. If I wanted to raise 2
operands clearer to a human reader. This is the first of many style guidelines I will to the power of 10+6, I bet-
recommend for making your programs easier to read. Style doesn’t change what ter put 10+6 in parentheses.
Otherwise it will be 2 raised
the program does; the MATLAB interpreter doesn’t check for style. But human
to the power of 10, then the
readers do, and the most important human who will read your code is you.
result of that calculation
And that brings us to the First Theorem of debugging: plus 6.
>> sin(1)
Figure 1.2 If you need to find a ans = 0.8415
command, use the “Search Doc-
umentation” text box in the top This command is an example of a function call. The name of the function is
right corner of the MATLAB win- sin, which is the usual abbreviation for the trigonometric sine. The value in
dow, or search the documentation parentheses is called the argument. All the standard trig functions in MATLAB
online.
work in radians. If you wish to work in degrees, you can: 1) covert from degrees to
radians with the conversion factor 180/π, or 2) you can add a “d” to the end of
the function. For example, while sin works in radians, sind works in degrees.
Some functions take more than one argument, in which case they are sepa-
rated by commas. For example, atan2 computes the inverse tangent, which is
the angle in radians between the positive x-axis and the point with the given y
and x coordinates.
>> atan2(1,1)
ans = 0.7854
If that bit of trigonometry isn’t familiar to you, don’t worry about it. It’s just an
example of a function with multiple arguments.
MATLAB also provides exponential functions, like exp, which computes e
raised to the given power. So exp(1) is just e.
>> exp(1)
ans = 2.7183
t I emphasize that the func- The inverse of exp is log, which computes the logarithm with base e:
tion log computes the log-
arithm with base e; what >> log(exp(3))
you likely think of as ln. ans = 3
To compute a logarithm
with base 10, use the func-
tion log10. You might also
This example also demonstrates that function calls can be nested; that is, you can
find the Change-of-Base use the result from one function as an argument for another.
Formula useful. More generally, you can use a function call as an operand in an expression.
manual. To learn about other functions, you should read the documentation. In
fact, whenever you use a function for the first time, I would encourage you to read
the function’s documentation page.
1.3 Documentation
MATLAB comes packaged with two forms of documentation, help and doc.
The help command works from the Command Window ; just type help followed
by the name of a command.
t The blue underlined text is
>> help sin a hyperlink. Give it a click.
Here, if you were to click
sin Sine of argument in radians.
on asin, it is as if you had
sin(X) is the sine of the elements of X. typed help asin.
appears in the help page in capital letters, but if you type it like that in MATLAB, under Display , you will find
a box that you can check
you get an error:
to “Suggest corrections for
mistyped functions and
>> SIN(1) variables.”
Undefined command/function 'SIN' for input arguments of type 'double'.
Notice that MATLAB is smart and suspects the typographical error, providing you
with a new command prompt with the correct function. In the current version
of MATLAB, boldface is used instead of capital letters to highlight the function
name in the documentation, to help prevent you from making this mistake.
Another problem is that the help page uses vocabulary you don’t know yet.
For example, “the elements of X” won’t make sense until we get to vectors
and matrices a few chapters from now.
The doc pages are usually better. If you type doc sin, a browser appears
with more detailed information about the function, including examples of how to
use it. The examples often use vectors and arrays, so they may not make sense
1 With MATLAB R2015a, lowercase “sin” is used in the Command Window . However, if I use
MATLAB R2015a from my Linux terminal, a non-GUI version that you would likely encounter at a
high performance computing center, capital letters are used for “SIN”! So even with newer versions,
it appears that you still need to be careful.
6 Chapter 1 Variables and values
yet, but you can get a preview of what’s coming. Whenever you encounter a new
function, I would strongly encourage you to get in the habit of looking at the doc
page. Remember, you can also access the documentation pages online.
1.4 Variables
t Technically pi is a function, One of the features that makes MATLAB more powerful than a calculator is the
not a variable. I only know ability to give a name to a value. A named value is called a variable.
this because if we type doc
MATLAB comes with a few predefined variables. For example the name pi
pi to bring up the docu-
mentation page, we find
refers to the mathematical quantity π, which is approximately
“pi returns the floating-
point number nearest the >> pi
value of π”. Since pi is a ans = 3.1416
function, it can be overrid-
den and used as a variable And if you do anything with complex numbers, you might find it convenient
name. This also means pi is
that both i and j are predefined as the square root of −1.
not stored in our Workspace .
But for now you can ignore
You can use a variable name anywhere you can use a number; for example, as
all this and pretend it is a an operand in an expression:
variable.
>> pi * 3^2
t Note: you can’t use Greek ans = 28.2743
letters in MATLAB; when
translating math expres- or as an argument to a function:
sions with Greek letters, it
is common to write out the >> sin(pi/2)
name of the letter (assum-
ans = 1
ing you know it).
But keep in mind that the value of ans changes every time you evaluate an ex-
pression.
1.5 Assignment statements 7
>> fibonacci0 = 1;
The first two examples demonstrate the use of the semi-colon, which sup-
presses the output from a command. In this case MATLAB creates the variables
and assigns them values, but displays nothing. The third example demonstrates
that not everything in MATLAB is a number. A sequence of characters in single
Figure 1.3 Workspace and His-
quotes is a string. tory windows. Notice variables
Although i, j and pi are predefined, you are free to reassign them. It is are listed in alphabetical and
common to use i and j for other purposes, but it is probably not a good idea to not chronological order in the
change the value of pi! Workspace window.
2. To make the connection between the code and the underlying mathematics
more apparent. If you are computing the area of a circle, you might want to
use a variable named r which corresponds to the radius:
t Two notes on breaking a
long expression onto mul-
>> r = 3
tiple lines using ...: 1) It
must follow after an operand r = 3
(i.e., +, –, *, /). 2) In the MAT-
LAB Command Window press- >> area = pi * r^2
ing Enter (or ) will cause area = 28.2743
MATLAB to evaluate the line
and will not have the desired
effect of continuing on to That way your code resembles the familiar formula πr 2 .
the next line. To do this, type
Shift + Enter . 3. To break a long computation into a sequence of steps. Suppose you are
evaluating a big, hairy expression like this:
You can use an ellipsis to break the expression into multiple lines. Just
type ... at the end of the first line and continue on the next. And in the
future if you forget about the use of an ellipsis, MATLAB conveniently has a
documentation page titled: “Continue Long Statements on Multiple Lines”.
(In case you haven’t gotten the hint by now, take advantage of MATLAB’s
documentation!)
But often it is better to break the computation into a sequence of steps and
assign intermediate results to variables.
The names of the intermediate variables explain their role in the computa-
tion. shiftx is the value of x shifted by theta. It should be no surprise that
exponent is the argument of exp, and denom ends up in the denominator.
Choosing informative names makes the code easier to read and understand
(see the First Theorem of Debugging on page 3).
1.7 Errors
It’s early, but now would be a good time to start making errors. Whenever you
learn a new feature, you should try to make as many errors as possible, as soon
1.7 Errors 9
as possible. When you make deliberate errors, you get to see what the error
messages look like. Later, when you make accidental errors, you will know what
the messages mean.
A common error for beginning programmers is leaving out the * for multipli-
cation.
The error message indicates that, after seeing the operand pi, MATLAB was
“expecting” to see an operator, like *. Instead, it got a variable name, which is the
“unexpected expression” indicated by the vertical line, | (which is called a “pipe”).
Another common error is to leave out the parentheses around the arguments
of a function. For example, in math notation, it is common to write something
like sin π, but not in MATLAB.
>> sin pi
Undefined function 'sin' for input arguments of type 'char'.
The problem is that when you leave out the parentheses, MATLAB treats the t Again, when in doubt, use
argument as a string (rather than as an expression). In this case the sin function parentheses.
generates a reasonable error message, but in other cases the results can be baf-
fling. For example, what do you think is going on here?
>> abs pi
ans = 112 105
There is a reason for this “feature”, but rather than get into that now, let me suggest
that you should always put parentheses around arguments.
This example also demonstrates the Second Theorem of Debugging:
The only thing worse than getting an error message is not getting an
error message.
Beginning programmers hate error messages and do everything they can to make
them go away. Experienced programmers know that error messages are your
friend. They can be hard to understand, and even misleading, but it is worth
making some effort to understand them.
Here’s another common rookie error. If you were translating the following
mathematical expression into MATLAB:
1
p
2 π
10 Chapter 1 Variables and values
>> 1 / 2 * sqrt(pi)
But that would be wrong. So very wrong. The correct translation would be:
>> 1 / (2 * sqrt(pi))
>> 1/3
ans = 0.3333
t You can also change the which is only approximately correct. It’s not quite as bad as it seems; MATLAB
format by selecting the uses more digits than it shows by default. You can change the format to see the
Preferences icon, then se- other digits.
lecting Command Window .
There is a drop-down list >> format long
next to “Numeric format”
>> 1/3
under “Text display”.
ans = 0.33333333333333
>> factorial(100)
ans = 9.332621544394410e+157
Although MATLAB can handle large numbers, there is a limit. The predefined
variables realmax and realmin contain the largest and smallest numbers that
MATLAB can handle.
>> realmax
ans = 1.797693134862316e+308
>> realmin
ans = 2.225073858507201e-308
>> factorial(170)
ans = 7.257415615307994e+306
>> factorial(171)
ans = Inf
>> realmax*100
ans = Inf
Division by zero and the natural log of zero returns Inf and -Inf, respectively.
>> 1/0
ans = Inf
>> log(0)
ans = -Inf
Allowing Inf to propagate through a computation doesn’t always do what you t In MATLAB R2009a and ear-
expect, but if you are careful with how you use it, Inf can be quite useful. lier, dividing by zero and
For operations that are truly undefined, MATLAB returns NaN, which stands the log of zero would pro-
for “not a number”. duce a warning message
because it is usually consid-
ered undefined. A warning
>> 0/0
is like an error message, but
ans = NaN the computation is allowed
to continue.
Lastly, we can change the format back to where we started:
We saw that with very large and small values, MATLAB uses scientific notation. If
12 Chapter 1 Variables and values
you would like to use scientific notation in general, you can change the format
to do so; you can augment short and long with an e:
1.9 Comments
Along with the commands that make up a program, it is useful to include com-
ments that provide additional information about the program. The percent
symbol % separates the comments from the code.
The comment runs from the percent symbol to the end of the line. In this case
it specifies the units of the value. In an ideal world, MATLAB would keep track
of units and propagate them through the computation, but for now that burden
falls on the programmer.
Comments have no effect on the execution of the program. They are there
for human readers. Good comments make programs more readable, but bad
comments are useless or (even worse) misleading.
Avoid comments that are redundant with the code:
Good comments provide additional information that is not in the code, like
units in the example above, or the meaning of a variable:
If you use longer variable names, you might not need explanatory comments,
but there is a trade-off: longer code can become harder to read. Also, if you are
translating from math that uses short variable names, it can be useful to make
your program consistent with your math.
1.10 Examples 13
1.10 Examples
In each chapter I will try to provide ample examples with worked solution. As you
work through the chapter material, you should first attempt to solve the example
problems without looking at the solution. Consult the solutions only if you get
stuck or to check your work. In doing so, the examples will serve as excellent
practice for the exercises at the end of the chapter. For additional help, most of
the examples will link to a screencast on my YouTube Channel of me working
through the solutions and making general comments on the Chapter material.
Often, at the end of the problem statement, I will provide you with a value of
the final numerical solution, ans =. You can use this value to troubleshoot/debug
your work. In later chapters, note that your answer may differ depending on the
numerical method used.
Example 1.1
Write a MATLAB expression that evaluates the following math expression. You can
assume that the variables µ, σ and x already exist.
x−µ 2
³ ´
− σp2
e
p (1.1)
σ 2π
Remember, you can’t use Greek letters in MATLAB; when translating math expres-
sions with Greek letters, it is common to write out the name of the letter (assuming
you know it).
To test your expression, use µ = 2.2, σ = 0.5, and x = 2.
(ans = 0.7365)
Solution: (Link to screen cast.) In my solution, I will break the calculation up into
many small parts to minimize error.
>> x = 2;
>> mu = 2.2;
>> sigma = 0.5;
>> exp_num = x-mu;
>> exp_den = sigma*sqrt(2);
>> exponent = -(exp_num/exp_den)^(2);
>> denominator = sigma*sqrt(2*pi);
>> exp(exponent) / denominator
ans = 0.7365
14 Chapter 1 Variables and values
Example 1.2
Professor Paluch is shopping for furniture. He finds a chair that cost $125 and a
sofa for $200. He decides to purchase 2 chairs and 1 sofa. Assuming he has a 20%
off coupon and sales tax is 8%, what is his total cost?
(ans = 388.80)
The result is properly round to two decimal places. Notice that MATLAB still
displays four decimal places. This is because of format short.
Example 1.3
¡π¢ ¡π¢
Compute: sin2 6 + cos2 6
(ans = 1)
The tricky part of this problem is the placement of the exponent. In our math
classes, we typically write sin2 (x) and cos2 (x). However, MATLAB does not recog-
nize this notation. What this means is that the (entire) quantity is squared, and
this is how we write it in MATLAB. In a math class, we prefer the other notation so
that we do not mis-interpret this as only the term in parentheses is squared.
Also note that we have an extra step in our order of operations here. First, MATLAB
evaluates the terms in parentheses, pi/6. Starting from the left, MATLAB then
evaluates the function, sin, and then squares the result. It then evaluates the
second term in the same fashion, and then adds the two together. If you were
unsure, you can add parentheses and get the same result
Example 1.4
Compute the area of a trapezoid with height of 2, and bases of 4 and 7. Remember:
A = 12 h (b 1 + b 2 ).
(ans = 11)
>> h = 2;
>> b1 = 4;
>> b2 = 7;
>> area = 0.5*h*(b1+b2)
area = 11
Example 1.5
Over limited temperature ranges, the vapor pressure of a pure fluid is commonly
correlated using an Antoine equation of the form
B
log10 p sat = A − (1.2)
T +C
where A, B , and C are constants. Equation (1.2) can also be solved for T to find the
corresponding saturation temperature at a particular pressure.
B
T= −C (1.3)
A − log10 p sat
16 Chapter 1 Variables and values
For ethanol, A = 8.13484, B = 1662.48, and C = 238.131 over the range −114.1 ◦ C <
T < 243.1◦ C, where T is in ◦ C and p sat is in mmHg. When using an Antoine
equation, please always be certain to check units; while units are not provided for
the constants, they are dependent on the units used for T and p sat
a) Calculate the vapor pressure of ethanol at 380 K in units of mmHg, atm, kPa,
and bar. The key to conversion is remembering atmospheric pressure: 1 atm
= 760 mmHg = 101.325 kPa. And 1 bar = 1 × 105 Pa, which is almost equal to 1
atm.
(2069.2 mmHg)
b) Does the vapor pressure of ethanol increase or decrease with increasing T ?
Find out by computing p sat at a few different temperatures.
(increases)
c) Calculate the normal boiling point of ethanol. That is, at what temperature
does it boil at atmospheric pressure?
(ans = 78.289)
a) The provided Antoine equation uses units of ◦ C for temperature and mmHg
for pressure. In this problem we are asked to compute the vapor pressure in
various units at 380 K. Let’s begin by converting the temperature to ◦ C, and
then using the Antoine equation as written to compute the vapor pressure in
mmHg. We can then convert to other units.
For convenience and to make my calculation more readable, I will store the
Antoine coefficients to lowercase a, b, and c, and I will make a note of the units
in the variables I use to store my temperature and pressure.
b) In my opinion, one of the best things about computational modeling is the abil-
ity to ask “what if” questions. To me, this becomes a very powerful educational
tool. For me, this is better than memorizing facts from a book.
Here, we are asked if the vapor pressure of ethanol increases or decreases
with temperature. From the previous question, we already have our model
set-up. Here we need only change the temperature and re-evaluate p_mmHg
= 10ˆ (a-b/(t_C+c)). Remember with the up arrow key ( ), we can quickly
recall previous commands, and are able to edit them too.
We find as temperature increases, the vapor pressure increases. Does this make
sense?
c) Last, we are asked to solve for the temperature where the vapor pressure is
equal to 1 atm or 760 mmHg. Remember, using the provided Antoine param-
eters, the temperature will be in ◦ C and the pressure must be in mmHg. Also,
the equation uses log10 . Remember in MATLAB log is actually the natural log,
ln or loge , and log10 is log10 . The constants are already stored in our session,
so we are all set to go!
The normal boiling point of ethanol is 78.289 ◦ C. This is less than the normal
boiling point of water of 100 ◦ C. Ethanol is therefore more volatile than water.
Example 1.6
All around us molecules are moving. It’s a hot summer’s day, and you sit down to
have a tall glass of ice water. How fast are water molecules moving in your glass? t In this problem and likely
Let’s calculate it! in many of your classes, you
need to know the values of
The kinetic energy of a molecule may be taken to be independent of it’s potential
several constants and con-
(or configurational) energy. This allows us to calculate the average kinetic energy
version factors. In future
of a molecule as
chapters we will build-up
to using scripts and then
m 2 3
Ek = v = kB T (1.4) functions to solve a prob-
2 2 lem. Then we can look our
constant up once to write a
function, which we can then
use over and over again.
18 Chapter 1 Variables and values
q s
3k B T
speed = v2 = (1.5)
m
So how fast are the molecules moving in your glass of ice water? Recall: R = 8.314
J/(mol·K), NAvo = 6.022 × 1023 mol−1 , and MWwater = 18 amu. You may assume
that the (solid) ice and liquid water are in equilibrium at 0 ◦ C. Report the speed in
both units of m/s and miles/hour. (1 mile = 1609.34 m)
(615.22 m/s)
Note, the average speed is only a function of T and m. For our problem then, since
the (solid) ice and liquid water are in equilibrium at the same T , the ice and liquid
water molecules are moving at exactly the same average speed!
At the same temperature, do heavier or lighter molecules move faster? Do molecules
move faster at higher or lower temperatures?
Having used SI units, this is the average speed in m/s. Now let’s convert to mph as
requested. We are provided with the conversion factor to go from m to miles. To
go from s to h, remember 60 s equal 1 min, and 60 min equal 1 h.
1.11 Chemical Engineering Examples 19
Wow! It is a good thing a molecule of water does not weigh too much.
At the same temperature, do heavier of lighter molecules move faster? The speed
is inversely proportional to mass, so we expect lighter molecules to move faster.
While we could work this out analytically, let’s use MATLAB and double the molec-
ular weight.
>> mw = 32/1000;
>> speed = sqrt( 3*r*t/mw )
speed = 461.41
>> t = 273.15*2;
>> speed = sqrt( 3*r*t/mw )
speed = 652.54
As the temperature increases, the speed increases. Not that in this last calculation,
the molecular weight was still double that of water.
Example 1.7
Shoot, an entry with exactly v = 0.1 m3 /kg is missing. But we do know that it must
lie somewhere between 600 and 650 ◦ C. We can estimate the temperature from
the provided data using a technique called linear interpolation. We will generate a
general solution which you can use in you CPB 314 class, and then apply it here for
the present problem.
Let’s start by setting up a table depicting the general scenario
20 Chapter 1 Variables and values
X Y
x1 y1
x2 y2
x3 y3
where X and Y are arbitrary properties (such as T and v), where the following
two points are known: (x 1 , y 1 ) and (x 3 , y 3 ). However, what you need to know
is y 2 for a specified x 2 (i.e. you need the point (x 2 , y 2 ) where x 2 is known). In
linear interpolation, we assume that the three points are co-linear (i.e., they all
lie on a straight line), where (x 2 , y 2 ) lies in between (x 1 , y 1 ) and (x 3 , y 3 ). This is an
assumption, one you should keep in mind.
What is the slope of a straight line? It is a constant. Therefore, the slope from
(x 1 , y 1 ) to (x 2 , y 2 ) is equal to the slope from (x 1 , y 1 ) to (x 3 , y 3 ). Remember from
your math classes that slope = m = rise/run = ∆y/∆x. Now let’s write it out using
math:
y2 − y1 y3 − y1
= (1.6)
x2 − x1 x3 − x1
All that is left is to solve for y 2 .
y3 − y1
y2 − y1 = (x 2 − x 1 ) (1.7)
x3 − x1
y3 − y1
y2 = (x 2 − x 1 ) + y 1 (1.8)
x3 − x1
You can simplify this expression as much or as little as you would like.
Now using our expression, go back and find the value of T where v = 0.1 m3 /kg.
(ans = 609.17)
>> v1 = 0.0989;
>> v2 = 0.1;
>> v3 = 0.1049;
>> t1 = 600;
>> t3 = 650;
>> t2 = (t3-t1)/(v3-v1)*(v2-v1)+t1
t2 = 609.17
1.13 Glossary
interpreter: The program that reads and executes MATLAB code.
prompt: The symbol the interpreter prints to indicate that it is waiting for you to
type a command.
operator: One of the symbols, like * and +, that represent mathematical opera-
tions.
argument: An expression that appears in a function call to specify the value the
function operates on.
nested function call: An expression that uses the result from one function call
as an argument for another.
22 Chapter 1 Variables and values
floating-point: The kind of number MATLAB works with. All floating-point num-
bers can be represented with about 16 significant decimal digits (unlike
mathematical integers and reals).
scientific notation: A format for typing and displaying large and small numbers;
e.g. 3.0e8, which represents 3.0 × 108 or 300,000,000.
• Construct a script to solve basic engineering problems Figure 2.1 The New Script button.
You could also find “New Script” in
If you work through the chapter and believe these goals are not met, please the drop-down list from the New
re-review the material and reach out for help. button.
So far we have typed all of our programs “at the prompt,” which is fine if you t There are also many other
are not writing more than a few lines. Beyond that, you will want to store your important reasons to write
program in a script and then execute the script. scripts, even “short” scripts.
A script is a file that contains MATLAB code. These files are also called “M- If there is a calculation you
files” because they use the extension .m, which is short for MATLAB. You can know you will need to per-
create and edit scripts with any text editor or word processor, but the simplest form many times, a script
can save you a great deal
way is by clicking the New Script button at the far left of the top menu under on
of time. Turning in a script
the Home tab. An Editor window appears running a text editor specially designed for a homework solution
for MATLAB. or lab report ensures that
others can reproduce your
work. It can also serve as a
lab notebook, documenting
exactly what you did.
23
24 Chapter 2 Scripts
t If you are using MATLAB Type the following code in the editor
Online, have a look back at
Section 1.12 and watch the x = 5
screen cast Introduction
to MATLAB Online from
and then press the large Save (outdated) floppy disk icon at the top left of the
Chapter 1: Variables and
values which includes an menu, or select the smaller Save icon in the smaller top right corner of your
example of downloading MATLAB desktop. You can also use your standard keyboard shortcuts, which for
myscript. Windows would be Ctrl + S . Either way, a dialog box appears where you can
choose the file name and the directory where it should go. Change the name to
myscript.m and leave the directory unchanged.
t Just as with functions, Tab
By default, MATLAB will store your script in your current folder. When you run
+ Tab (or + ) works
when typing the name of the script from the Command Window , MATLAB will look in your current folder
script files too. If you can for the file, and it will also look in the search path, which is the list of directories
remember just the first few MATLAB searches for scripts. You can see the list of directories in the search path
letters, it will try to auto-fill by clicking on the “Set Path” icon on the Home tab. 1
the rest. If there is more Go back to the Command Window and type myscript (without the extension)
than one possibility, a list at the prompt. MATLAB executes your script and displays the result.
of possible commands will
appear. If you get an error,
this is a good check if you >> myscript
mis-spelled the name or are x = 5
in the wrong directory.
When you run a script, MATLAB executes the commands in the M-File, one after
t In MATLAB and in general, another, exactly as if you had typed them at the prompt.
a script is nothing more If something goes wrong and MATLAB can’t find your script, you will get an
than a series of commands error message like:
one could just as well enter 1 If you are curious, information about adding and removing folders to/from the search path
one after the other in a
may be found in the MATLAB documentation for “Change Folders on the Search Path.” You can
command prompt (or here
also add folders to the search path interactively using the “Set Path” icon on the Home tab. You
the Command Window).
may find this useful when you become a more advanced MATLAB user.
2.1 M-files 25
>> myscript
Undefined function or variable 'myscript'.
In this case you can either need to save your script again in your current
directory or in a directory that is on the search path, or you need to modify the
search path to include the directory where you keep your scripts. Changing your
current folder is easily accomplished by using the interactive path navigator found
immediately above your windows, but below the menu icons. You can also use
the Current Folder window to interactively navigate within your current folder.
When you are first getting started writing scripts, this discussion of current
folders and paths can be a little confusing. And from my experience teaching this
course, a very common (and frustrating) mistake made by students is saving files
in a directory other than the current folder. If the reading was unclear, have a look
at my “Chapter 2: Current folder and paths” screen cast.
The file name can be anything you want, but you should try to choose some-
thing meaningful and memorable. You should be very careful to choose a name
that is not already in use; if you do, you might accidentally replace one of MAT-
LAB’s functions with your own. Finally, the name of the file cannot contain spaces.
If you create a file named my script.m, MATLAB doesn’t complain until you try
to run it:
t We saw previously on
>> my script page 5 that when we typed
SIN, MATLAB suggested
Undefined function or variable 'my'.
it was a possible typo and
asked if we meant sin. If
The problem is that it is looking for a script named my. The problem is even a script (or function) with
worse if the first word of the file name is a function that exists. Just for fun, create a similar name is present
a script named abs val.m and run it. Remember, if you would like the effect of a in your path, MATLAB may
space in your file name, use an underscore (_) between abs and val. make a suggestion.
Before looking at an example, in previous semesters I have found that students
like to use the “Run” button to execute their scripts. Simply clicking the run button
can also be used to run functions without any inputs. It is equivalent to typing
the name of the M-file in the Command Window . The Run button is located on the
Editor tab. In general, simply clicking the Run button will work for scripts and
functions with no inputs. I tend to avoid using the Run button. This way when
we deal later with functions, I am certain to assign the output to a variable other
Figure 2.4 The large “Run” button
than “ans”, and I can make sure I pass any variables that need to be passed. But on the Home tab. Simply clicking
more on that later. For now, if you are curious, have a look at my Chapter 2: Run the Run button is equivalent to
Button screen cast. typing the name of the M-file in
the Command Window .
26 Chapter 2 Scripts
Example 2.1
Save the script to your current path. Then to compute the n th Fibonacci number:
>> n = 10;
>> fibonacci1_long
ans = 55.0000
>> fibonacci1
ans = 55.0000
2.2 Why scripts? 27
• If you choose good names for your scripts, you will be able to remember
which script does what, and you might be able to reuse a script from one
project to the next. Similarly, don’t “reinvent the wheel” every time you
solve a problem; if you have script for a similar problem, use it as your
starting point and modify it as necessary. We will revisit this idea later in
this chapter on page 35 as the first step of incremental development.
• If you run a script repeatedly, it is faster to type the name of the script than
to retype the code!
Unfortunately, the great power of scripts comes with great responsibility, which is
that you have to make sure that the code you are running is the code you think
you are running.
First, whenever you edit your script, you have to save it before you run it. If
you forget to save it, you will be running the old version. Also, whenever you start
a new script, start with something simple, like x=5, that produces a visible effect.
Then run your script and confirm that you get what you expect. MATLAB comes
with a lot of predefined functions. It is easy to write a script that has the same
name as a MATLAB function, and if you are not careful, you might find yourself
running the MATLAB function instead of your script. This will also make sure you
are saving in the search path. Either way, if the code you are running is not the
code you are looking at, you will find debugging a frustrating exercise! And that
brings us to the Third Theorem of Debugging:
t So start by making sure your
script actually runs. Then
You must always be 100% sure that the code you are running is the
if problems arise, you can
code you think you are running. focus on the the code itself.
>> x=5;
>> y=7;
>> z=9;
>> who
>> clear y
>> who
Figure 2.5 If you prefer a graphical
environment, don’t forget about Your variables are:
the Workspace window. To clear x z
a variable, you can highlight it by
clicking, then hit the Delete key on You can clear more than one variable at a time by listing the variables sequen-
your keyboard, or right-click with tially.
your mouse then select “Delete”.
If you right-click, you will also
find other useful commands that >> y = 7;
should be self-explanatory. >> clear x y
>> who
If you wish to clear all of the variables, you could list all of the variables sequen-
tially, or for this specific case you can use the command clear variables.
>> x=5;
>> y=7;
>> clear variables
>> who
To display the value of a variable, you can use the disp function.
>> z=9;
>> disp(z)
9
>> z
z = 9
2.4 More errors 29
>> fibonacci1
Undefined function or variable 'n'.
The details of this message might be different for you, depending on what’s in
your script. But the general idea is that n is undefined. Notice that MATLAB tells
you what line of your program the error is in, and displays the line.
This information can be helpful, but beware! MATLAB is telling you where
the error was discovered, not where the error is. The reported line is where n is
first used. And since it is not defined, MATLAB stops at this point. In this case,
the error is not in the script at all; it is, in a sense, in the workspace.
Which brings us to the Fourth Theorem of Debugging:
Error messages tell you where the problem was discovered, not
where it was caused.
The object of the game is to find the cause and fix it—not just to make the error
message go away.
fibonacci1.m
1 %
2 % Computing the nth Fibonacci number using equation 2.1
3 % Precondition: you must assign a value to n before
4 % running this script.
5 % Postcondition: the result is stored in ans.
6 %
A precondition is something that must be true, when the script starts, in order
for it to work correctly. A postcondition is something that will be true when the
script completes.
If there is a comment at the beginning of a script, MATLAB assumes it is
the documentation for the script, so if you type help fibonacci1, you get the
contents of the comment (without the percent signs).
>> y = 1;
>> x = y+1
x = 2
>> y+1 = x
y+1 = x
|
Error: The expression to the left of the equals sign is not a
valid target for an assignment.
2.6 Assignment and equality 31
In this case the error message is pretty helpful, as long as you know what a “target”
is.
Another difference is that an assignment statement is only temporary, in the
following sense. When you assign x = y+1, you get the current value of y. If y
changes later, x does not get updated.
A third difference is that a mathematical equality is a statement that may or
may not be true. For example, y = y + 1 is a statement that happens to be false
for all real values of y. In MATLAB, y = y+1 is a sensible and useful assignment
statement. It reads the current value of y, adds one, and replaces the old value
with the new value.
>> y = 1;
>> y = y+1
y = 2
When you read MATLAB code, you might find it helpful to pronounce the
equals sign “gets” rather than “equals.” So x = y+1 is pronounced “x gets the
value of y plus one.”
To test your understanding of assignment statements, try this exercise:
32 Chapter 2 Scripts
Example 2.2
Write a few lines of code that swap the values of x and y. Put your code in a script
called swap and test it.
Solution: (Link to screen cast with accompanying M-file.) Let’s start by writing the
script.
Listing 2.3 swap.m
1 %
2 % Swap the current value of x with the current value of y
3 % Precondition: you must assign a value to x and y before running
4 % this script.
5 % Postcondition: the initial value of y is stored as the new value
6 % of x.
7 % the initial value of x is stored as the new value
8 % of y.
9 %
10
11 % Storing a copy of the intial value of y
12 y0 = y;
13
14 % Now re-assigning
15 y = x
16 x = y0
17
18 % And now clearing (or deleting) the new variable y0 that we created
19 clear y0;
>> y = 2;
>> x = 4;
>> swap
y = 4
x = 2
2.6 Assignment and equality 33
Example 2.3
Imagine that you are the owner of a car rental company with two locations, Albany
and Boston. Some of your customers do “one-way rentals,” picking up a car in
Albany and returning it in Boston, or the other way around. Over time, you have
observed that each week 5% of the cars in Albany are dropped off in Boston, and
3% of the cars in Boston get dropped off in Albany. At the beginning of the year,
there are 150 cars at each location.
Write a script called car_update that updates the number of cars in each loca-
tion from one week to the next. The precondition is that the variables a and b
contain the number of cars in each location at the beginning of the week. The
postcondition is that a and b have been modified to reflect the number of cars that
moved.
To test your program, initialize a and b at the prompt and then execute the script.
The script should display the updated values of a and b, but not any intermediate
variables.
Note: cars are countable things, so a and b should always be integer values. You
might want to use the round function to compute the number of cars that move
during each week.
If you execute your script repeatedly, you can simulate the passage of time from
week to week. What do you think will happen to the number of cars? Will all the
cars end up in one place? Will the number of cars reach an equilibrium, or will it
oscillate from week to week?
In the next chapter we will see how to “automatically” execute your script repeat-
edly, and how to plot the values of a and b versus time.
(After 1 week: a = 147 and b = 153)
Let’s simulation the passage of a few weeks here. On your own you can try more.
>> b = 150;
>> a = 150;
>> car_update % Week 1
b = 153
a = 147
a = a - 0.05*a + 0.03*b
b = b + 0.05*a - 0.03*b
But that would be wrong, so very wrong. Why? The problem is that the first line
changes the value of a, so when the second line runs, it gets the old value of b
and the new value of a. As a result, the change in a is not always the same as the
change in b, which violates the principle of Conversation of Cars!
One solution is to use temporary variables anew and bnew:
This has the effect of updating the variables “simultaneously;” that is, it reads
both old values before writing either new value.
The following is an alternative solution that has the added advantage of sim-
plifying the computation:
It is easy to look at this code and confirm that it obeys “Conversation of Cars”.
Even if the value of atob is wrong, at least the total number of cars is right. And
that brings us to the Sixth Theorem of Debugging:
In this case, removing redundancy also eliminates the opportunity for a bug.
1. Always start with a working program. If you have an example from a book
or a program you wrote that is similar to what you are working on, start
with that. Otherwise, start with something you know is correct, like x=5.
Run the program and confirm that you are running the program you think
you are running.
This step is important, because in most environments there are lots of little
things that can trip you up when you start a new project. Get them out of
the way so you can focus on programming.
2. Make one small, testable change at a time. A “testable” change is one that
displays something on the screen (or has some other effect) that you can
check. Ideally, you should know what the correct answer is, or be able to
check it by performing another computation.
3. Run the program and see if the change worked. If so, go back to Step 2. If
not, you will have to do some debugging, but if the change you made was
small, it shouldn’t take long to find the problem.
When this process works, you will find that your changes usually work the first
time, or the problem is obvious. That’s a good thing, and it brings us to the Fifth
Theorem of Debugging:
The best kind of debugging is the kind you don’t have to do.
• Sometimes you have to write extra code to generate visible output that you
can check. This extra code is called scaffolding because you use it to build
the program and then remove it when you are done. But the time you save
on debugging is almost always worth the time you spend on scaffolding.
36 Chapter 2 Scripts
• When you are getting started, it is usually not obvious how to choose the
steps that get from x=5 to the program you are trying to write.
If you find yourself writing more than a few lines of code before you start
testing, and you are spending a lot of time debugging, you should try incremental
development.
>> asin(0)
ans = 0
which is correct. Also, we know that the sine of 90 degrees is 1, so if we try asin(1),
we expect the answer to be 90, right?
>> asin(1)
ans = 1.5708
t Note, we could alternatively Oops! We forgot that the trig functions in MATLAB work in radians, not degrees.
use asind for degrees. So the correct answer is π/2, which we can confirm by dividing through by pi:
>> asin(1) / pi
ans = 0.5000
With this kind of unit testing, you are not really checking for errors in MATLAB,
you are checking your understanding. If you make an error because you are
confused about how MATLAB works, it might take a long time to find, because
when you look at the code, it looks right.
Which brings us to the Seventh Theorem of Debugging:
The worst bugs aren’t in your code; they are in your head.
While this is one example of unit testing, you might also think of a large pro-
gramming project as a block flow diagram encountered in your mass and energy
balance course; work on each unit operation (or block) separately, one at a time,
then combine them to model the entire process.
Syntax error: You have written a MATLAB command that cannot execute be-
cause it violates one of the rules of syntax. For example, you can’t have two
operands in a row without an operator, so pi r^2 contains a syntax error.
When MATLAB finds a syntax error, it prints an error message and stops
running your program.
Runtime error: Your program starts running, but something goes wrong along
the way. For example, if you try to access a variable that doesn’t exist, that’s
a runtime error. When MATLAB detects the problem, it prints an error
message and stops.
Logical error: Your program runs without generating any error messages, but it
doesn’t do the right thing. The problem in Section 2.7, where we changed
the value of a before reading the old value, is a logical error.
Syntax errors are usually the easiest. Sometimes the error messages are con-
fusing, but MATLAB can usually tell you where the error is, at least roughly.
Run time errors are harder because, as I mentioned before, MATLAB can tell
you where it detected the problem, but not what caused it.
Logical errors are hard because MATLAB can’t help at all. Only you know what
the program is supposed to do, so only you can check it. From MATLAB’s point of
view, there’s nothing wrong with the program; the bug is in your head!
Numerical errors can be tricky because it’s not clear whether the problem
is your fault. For most simple computations, MATLAB produces the floating-
point value that is closest to the exact solution, which means that the first 15
significant digits should be correct. But some computations are ill-conditioned,
which means that even if your program is correct, the roundoff errors accumulate
and the number of correct digits can be smaller. Sometimes MATLAB can warn
you that this is happening, but not always! Precision (the number of digits in the
answer) does not imply accuracy (the number of digits that are right). Numerical
errors is a topic that will come up repeatedly in this course.
38 Chapter 2 Scripts
Example 2.4
Let’s revisit Example 1.5 on example 1.5. Write two scripts to calculate the vapor
pressure (eq. (1.2)) and saturation temperature (eq. (1.3)) of a pure fluid using
Antoine’s equation. Since you anticipate using Antoine’s equation to model a wide
range of fluids, as a precondition require that the variables a, b and c contain your
Antoine parameters, and that variable tsat or psat contain your temperature or
pressure, depending on if you would like to compute p sat or T .
Remember that the Antoine parameters are dependent on the units of p sat and T .
I would therefore be sure to include this information as a comment at the top of
your script so that a user can find this information using help. Tell the user what
the precondition units of tsat and psat should be. They don’t necessarily need
to agree with the Antoine equation; you can perform the unit conversion at the
beginning of the script file. Use units that you believe will be most useful. Same is
true for the postcondition value of psat or tsat, which ever you are calculating.
You can perform the Antoine calculations in ◦ C and mmHg, then convert to your
preferred units. Again, just include this information as a comment at the start of
your script file. (For problem a, you might consider outputting the pressure in a
range of units.)
Next, let’s use our scripts to solve again Example 1.5 a), b) and c). For ethanol,
A = 8.13484, B = 1662.48, and C = 238.131 over the range −114.1 ◦ C < T < 243.1◦ C,
where T is in ◦ C and p sat is in mmHg.
a) Calculate the vapor pressure of ethanol at 380 K in units of mmHg, atm, kPa,
and bar. The key to conversion is remembering atmospheric pressure: 1 atm
2.12 CPB Examples 39
= 760 mmHg = 101.325 kPa. And 1 bar = 1 × 105 Pa, which is almost equal to 1
atm. (Note, be sure to have your script perform the temperature conversion.)
(2069.2 mmHg)
b) Does the vapor pressure of ethanol increase or decrease with increasing T ?
Find out by computing p sat at a few different temperatures.
(increases)
c) Calculate the normal boiling point of ethanol. That is, at what temperature
does it boil at atmospheric pressure?
(ans = 78.289)
a) Let’s start by writing the script. I will keep the script general and ask the user to
provide Antoine parameters. This way we can apply the script to other systems.
You could just as well include the parameters in the script, both are correct.
Listing 2.5 Example_2_4a.m
1 % Calculating the vapor pressure of ethanol using Antione's equation
2 %
3 % Dr. Paluch, Example 2.4a
4 %
5 % Pre-condition: A, B, and C (for Antione's equation which uses mmHg and
6 % oC) stored to variables a, b, and c, and the temperature
7 % in K stored to variable t_K.
8 % Post-condition: Psat in units of mmHg (p_mmHg), atm (p_atm),
9 % kPa (p_kPa), and bar (p_bar), and temperature in
10 % degrees C (t_C)
11 %
12
13 % First, let's convert T from oC to K
14 t_C = t_K-273.15;
15 % Calcutating log_p, with p in mmHg
16 log_p_mmHg = a-b/(t_C+c);
17 % p in mmHg
18 p_mmHg = 10^log_p_mmHg
19 % p in atm
20 p_atm = p_mmHg/760
21 % p in kPa
22 p_kPa = p_atm*101.325
23 % p in bar
24 p_bar = p_kPa/100
p_mmHg = 2069.2
p_atm = 2.7226
p_kPa = 275.86
p_bar = 2.7586
Once again, we find that as the temperature increases, the vapor pressure
increases.
c) Similar to part (a), let’s start by writing the script, where again we will keep the
script as general as possible to facilitate extension to other fluids.
Listing 2.6 Example_2_4c.m
1 % At a given pressure, calculate the correspnoding boiling point
2 % (or saturation temperature) using Antione's equation.
3 %
4 % Dr. Paluch, Example 2.4c
5 %
6 % Pre-condition: A, B, and C (for Antione's equation which uses mmHg and
7 % oC) stored to variables a, b, and c, and the pressure
8 % in units of atm stored to p_atm.
9 % Post-condition: The saturation temperature in units of oC (t_C)
10 %
11 % Convert the inputed pressure from atm to mmHg
12 p_mmHg = p_atm*760;
13 % Calculating the saturation temperature
14 t_C = b/(a-log10(p_mmHg))-c
The Antoine parameters were already defined in our workspace in part (a), so
here we need just specify the pressure for which we desire to know the satura-
tion temperature, in this case 1 atm. Take note of the units used; I use units of
2.12 CPB Examples 41
atm for pressure for the input pressure, and then convert to mmHg in the script.
>> p_atm = 1;
>> Example_2_4c
t_C = 78.289
In this exercise, the instructions stated: “Since you anticipate using Antoine’s equa-
tion to model a wide range of fluids, as a precondition require that the variables a,
b and c contain your Antoine parameters, and that variable tsat or psat contain
your temperature or pressure.” However, know that this is not always the best
practice. I tell you to do so here as an academic exercise and to become familiar
the interactions of scripts with your workspace. But if I think of a submission for
homework, lab or project, I would create a script where at least the Antoine param-
eters are hard coded, and I might update the name of the file to contain the name
of the species whose Antoine parameters are provided. Then if someone wanted
to look at another fluid, they could easily save a copy of the file and update the
parameters, assuming you have clear documentation. And if for your homework
you were looking at only a specific temperature or pressure, I would hard code that
too.
Example 2.5
Revisit Example 1.6 on example 1.6 and write a script to calculate the average speed
of a molecule at a given temperature using eq. (1.5). As a precondition, require that
the molecular weight of the molecule of interest and the temperature are stored
to variables. Please be sure to communicate the expected variable names and
their units at the start of your script as a comment, so the user can see them with
the help command. Please also communicate the units of the (postcondition)
computed speed.
How fast are the molecules moving in a glass of ice water? The molecular weight of
water is MWwater = 18 amu, and you may assume that the temperature is 0 ◦ C.
What if your glass also contained liquid ethanol molecules at the same temperature.
Would the ethanol molecules or water molecules be moving faster? By how much?
The molecular weight of ethanol is MWethanol = 46 amu.
>> mw = 18;
>> t_C = 0;
>> Example_2_5
speed_mps = 615.22
We find that the average speed of water is greater than water, as expected since
water has the smaller mass. At 0 ◦ C, we find that the average speed of water is
1.5986 times larger than ethanol.
2.12 CPB Examples 43
Example 2.6
Many of you are currently taking engineering thermodynamics or will take it next
semester. Let’s therefore revisit Example 1.7 on example 1.7 and write a script
to perform linear interpolation. Hopefully you will find the resulting script to be
useful in your class. In your script, please be sure to communicate the required
precondition input. To be absolutely clear, I might suggest including a table like
that used to derive the general expression in Example 1.7.
Test your code on Example 1.7: You would like to know the temperature of su-
perheated steam at 40 bar and with a molar volume of v = 0.1 m3 /kg. In the
“Superheated Steam” tables in the back of your engineering thermodynamics text,
you find the following data
T (◦ C) v (m3 /kg)
600 0.0989
650 0.1049
An entry with exactly v = 0.1 m3 /kg is missing. However, we know that it must lie
somewhere between 600 and 650 ◦ C. Estimate the temperature from the provided
data using linear interpolation.
>> x1 = 0.0989;
>> x2 = 0.1;
>> x3 = 0.1049;
>> y1 = 600;
>> y3 = 650;
>> Example_2_6
y2 = 609.17
2.13 Glossary
M-file: A file that contains a MATLAB program.
search path: The list of directories where MATLAB looks for M-files.
precondition: Something that must be true when the script starts, in order for it
to work correctly.
scaffolding: Code you write to help you program or debug, but which is not part
of the finished program.
2.14 Exercises
Exercise 2.1 “Yaws’ Critical Property Data for Chemical Engineers and Chemists”2 rec-
ommends the following equation to compute the surface tension of saturated organic
compounds (i.e., organic compounds at vapor/liquid equilibrium):
σ = A + BT +C T 2 + DT 3 + E T 4 + F T 5 (2.2)
where σ is the surface tension in units of N/m, T is the temperature in K, and A, B , C ,
D, E , and F are regressed coefficients. The surface tension is the force per unit length
needed to separate the molecules at the vapor/liquid interface. It can equivalently be
related to the vapor/liquid free energy barrier per unit area:
1F
σ= (2.3)
2A
At the critical point, the two phases become one, and the vapor/liquid free energy bar-
rier and hence surface tension go to zero. This is a famous property that Guggenheim
exploited to estimate the critical temperature of compounds. He would measure the
surface tension at a range of accessible temperatures, and then extrapolate to where the
surface tension went to zero. Cool!
a) Write a script to compute surface tension using the expression suggested by Yaws’.
b) Using your script, compute the surface tension of propane, ethanol, and acetone at
a range of temperatures. Does surface tension increase or decrease with increasing
temperature? Does the surface tension appear to approach zero at the critical point?
(Tmax corresponds to the critical temperature.)
c) Propane is a linear alkane consisting of three methyl groups. We can picture ethanol as
propane where one of the terminal methyl groups has been replaced with a hydroxyl
group. Comparing the surface tension of propane and ethanol can therefore give us an
idea of the difference in intermolecular interaction strength of a methyl and hydroxyl
group. Compute the surface tension of propane and ethanol at 300 K, and compare. In
which system are the intermolecular interactions strongest? Next compare to acetone.
What is the effect of the carbonyl group.
2 Yaws, Carl L. (2012; 2013; 2014). Yaws’ Critical Property Data for Chemical Engineers
and Chemists. Knovel. Retrieved from https://app.knovel.com/hotlink/toc/id:kpYCPDCECD/
yaws-critical-property/yaws-critical-property
46 Chapter 2 Scripts
Exercise 2.2 “Yaws’ Critical Property Data for Chemical Engineers and Chemists” recom-
mends use of a modified Watson equation to calculate the enthalpy of vaporization as a
function of temperature:
T n
µ ¶
∆H vap = A 1 − (2.4)
B
where A, B and n are regressed coefficients, T is the temperature in K, and ∆H vap is
the enthalpy of vaporization in units of kJ/mol. ∆H vap corresponds to the change in
enthalpy in going from a liquid to vapor, where the two phases are at equilibrium (same
temperature and pressure). Like surface tension, ∆H vap can be related to intermolecular
interactions. If we assume the vapor phase is an ideal gas (a very good approximation at
atmospheric pressure), then:
B
log10 S = A + +C log10 T (2.6)
T
where S is the solubility in water in parts per million by weight (ppm), T is the temperature
in K, and A, B , and C are regressed coefficients.
Let s be the solubility in water in parts per million by moles. We can convert from
S to s using the molecular weight of the solute (M s ) and the molecular weight of water
(M w ) as:
S/M s
s= ¡ ¢ (2.7)
S/M s + 1 × 106 − S /M w
The molecular weight of water is 18.015 g/mol, and the molecular weight of the solutes
are provided in the table below.
a) Write a script to compute the solubility using the expression suggested by Yaws’ in
units of ppm by weight and by mole (S and s).
b) Using your script, compute the solubility of methane, ethane, and propane at a range
of temperatures. Does the solubility of a gas in water increase or decrease with
increasing temperature?
c) Compare the solubility in ppm by mole (s) of methane, ethane, and propane at
a common temperature of 298.15 K. Does the solubility increase or decrease with
increasing alkyl chain length? Put differently, does the solubility increase or decrease
in going from methane to ethane to propane?
Exercise 2.4 In your transport phenomenon course (fluid mechanics), you likely solved
many problems that required you to read values of friction constants from a Moody
chart, or to use analytic expressions for the friction factor of limited range. Recently,
Díaz-Damacillo and Plascencia published an article in AIChE Journal titled: “A New Six
Parameter Model to Estimate the Friction Factor.”4 In that work, the authors propose
a new analytic expression containing six parameters that is capable of estimating the
friction factor for flow in pipes at all conditions (i.e., Reynold’s numbers and relative
roughness). The proposed expression takes the form:
64 λ1 λ2
f = + ´+ (2.8)
Re 1 + exp τ1 −Re 1 + exp τ2 −Re ·
³ ³ ´
²
100 600 D
ρV D
Re =
µ
where ρ is the density of the fluid, V is the fluid flow velocity, and D is the diameter of the
pipe. The term ² is the pipe roughness, and the term ²/D is dimensionless and commonly
referred to as the relative roughness. In addition to Re and ²/D (two parameters), the other
six parameters are λ1 , λ2 , τ1 , and τ2 . The parameter λ1 is the residual stress contribution
from the laminar to turbulant transition to the friction factor, λ2 is the residual stress
contribution from the pipe roughness to the friction factor, τ1 is the value of Re at which
occurs the first transition in the friction factor, and τ2 is the value of Re at which the
second transition occurs. The values of λ1 and τ1 are constant and equal to:
λ1 = 0.02
τ1 = 3000
and λ2 and τ2 are given by the following expressions:
¯ Ã !2 ¯
¯ 1 ¯
λ2 = ¯λ1 −
¯ ¯
1 ²
¡ ¢ ¯
¯ −2 log 10 ·
3.7065 D
¯
0.77505 10.984
τ2 = ¡ ¢2 − ¡ ² ¢ + 7953.8
²
D D
As you try to keep track of units, remember that Re and ²/D are dimensionless.
a) Write a script that computes f for known values of Re and ²/D. To test your code, for
Re = 1 × 105 with ²/D = 1/30 I get f = 0.0601.
b) Using your script, does f increase or decrease when Re increases?
c) Using your script, does f increase or decrease when ²/D increases?
Chapter 3
for loops and basic plotting
In Chapter 3 we continue to build our foundational knowledge of MATLAB with t Let’s interpret the for loop
the introduction of loops and plotting. By the end of this chapter you will be able another way. For the case
to: 1 ≤ i ≤ 52, execute the
command(s) between the
• Define a for loop and explain how it works for and end statements.
Initially, when we first en-
• Explain how to construct a for loop to automate repetitive calculations counter the for statement,
i is assigned a value of 1 (or
• Demonstrate the ability to construct basic plots
whatever integer you set the
• Apply the use of for loops and plotting to solve basic engineering problems lower-bound to be). Then
we execute car_update
If you work through the chapter and believe these goals are not met, please and reach end. Here, the
re-review the material and reach out for help. computer asks if i=52 (or
whatever integer you set
the upper-bound of the list
3.1 for loops to be). If i=52 we exit and
move on in the code. How-
A loop is a part of a program that executes repeatedly; a for loop is the kind of ever, if i<52, we go back to
loop that uses the for statement. for, increment the value of
i by one (i = i + 1), then
The simplest use of a for loop is to execute one or more lines a fixed num-
execute car_update, and
ber of times. For example, in Example 2.3 (page 33) we wrote a script named
then ask again if i=52. This
car_update (Listing 2.4) that simulates one week in the life of a rental car com- repeats until i=52 when the
pany. To simulate an entire year, we have to run it 52 times: end statement is reached.
49
50 Chapter 3 for loops and basic plotting
The colon operator, :, specifies a range of integers. In the spirit of unit testing,
you can create a range at the prompt:
>> 1:5
ans = 1 2 3 4 5
The variable you use in the for statement is called the loop variable. It is a
common convention to use the names i, j and k as loop variables.
The statements inside the loop are called the body. By convention, they are
indented to show that they are inside the loop, but the indentation does not
actually affect the execution of the program. The end of the loop is officially
marked by the end statement.
Let’s look at a simple example that we can run from the command line that
displays the value of the loop variable:
t When writing a for loop
from command line, you >> for i=1:5
do not need to press
i
Shift + Enter to get to the
next line. MATLAB recog-
end
nizes that the loop is “open”,
and will not evaluate until i = 1
you press Enter after the i = 2
end statement. Remember i = 3
MATLAB skips whitespace, i = 4
so I add spaces to make my
i = 5
command more readable.
Does the result make sense? This simple example allows us to test our under-
standing of the flow of calculations in a for loop, and is a perfect example of
using the Command Window for unit testing and debugging.
What is the value of i after the loop is complete? Let’s unit test from the
command line to find out:
>> i
i = 5
The current value of i is the last value assigned before exiting the loop. The great
thing about computers is they do exactly what you tell them to. The bad thing
is they do exactly what you tell them to, even if it is wrong. So take time to fully
understand the flow of calculations through a for loop. Please try to get in the
habit of unit testing from the command line. It is an invaluable tool to help you
check your understanding of MATLAB.
As this example shows, you can run a for loop from the command line, but
it’s much more common to put it in a script. If this discussion has been unclear,
please watch the available screen cast.
When learning about a new feature of MATLAB, it is always a great idea to look
3.1 for loops 51
at the MATLAB documentation pages. Here, try doc for. For this specific case,
the documentation will use features we haven’t yet discussed in this class, such as
vectors and matrices. But with time it will. You will also get an idea of some more
advanced capabilities that we will take advantage of later, once we have honed
our skills a little more, such as break and continue. The documentation is great
too because it will link to related commands and keywords.
Example 3.1
Create a script named car_loop that uses a for loop to run car_update (List-
ing 2.4 on page 2.4) 52 times. Remember that before you run car_update, you
have to assign values to a and b. For this exercise, start with the values a = 150
and b = 150. Also, make sure that both car_update and car_loop are in your
current path so that they can “see” each other.
If everything goes smoothly, your script will display a long stream of numbers on
the screen. But it is probably too long to fit, and even if it fits, it would be hard to
interpret. A graph would be much better!
(Running, I get a final answer of b = 184 and a = 116.)
Solution: (Link to scree cast with accompanying M-files.) The script is a straight-
forward for loop. We want to run the script car_update 52 times to simulation
the passage of cars over a period of 1 year or 52 weeks.
Listing 3.1 car_loop.m
1 %
2 % Update the number of cars in Albany and Boston over the
3 % course of 52 weeks.
4 % Precondition: you must assign the number of cars in Albany and Boston
5 % at the start of the week to a and b, respectively.
6 % Postcondition: a and b have been modified to reflect the number of
7 % cars that moved after 1 year (52 weeks).
8 %
9 for i=1:52
10 car_update
11 end
In the interest of space I will not display the output here, but the script would be
run as:
>> b = 150;
>> a = 150;
>> car_loop
Note that since car_loop references the script car_update, it is important that
both scripts are in the same directory, and in your current path. Running the script,
I get a final answer of b = 184 and a = 116. In the next section, we will see how
the results may be plotted.
52 Chapter 3 for loops and basic plotting
In the interest of checking our understanding, in Example 2.3 (page 33), we simu-
lated the passage of three weeks. Let’s write a script car_loop_short to reproduce
the results.
Listing 3.2 car_loop_short.m
1 %
2 % Update the number of cars in Albany and Boston over the
3 % course of 52 weeks.
4 % Precondition: you must assign the number of cars in Albany and Boston
5 % at the start of the week to a and b, respectively.
6 % Postcondition: a and b have been modified to reflect the number of
7 % cars that moved after 3 weeks.
8 %
9 for i=1:3
10 car_update
11 end
>> b = 150;
>> a = 150;
>> car_loop_short
b = 153
a = 147
b = 155
a = 145
b = 157
a = 143
Nice! This will be rather primitive and not as pretty as MATLAB is capable, but we
could use disp to indicate the week to make the output more readable by a user.
Listing 3.3 car_loop_short2.m
1 %
2 % Update the number of cars in Albany and Boston over the
3 % course of 52 weeks.
4 % Precondition: you must assign the number of cars in Albany and Boston
5 % at the start of the week to a and b, respectively.
6 % Postcondition: a and b have been modified to reflect the number of
7 % cars that moved after 3 weeks.
8 %
9 for i=1:3
10 disp('Week: ')
11 disp(i)
12 car_update
13 end
>> b = 150;
>> a = 150;
>> car_loop_short2
3.2 plotting 53
Week:
1
b = 153
a = 147
Week:
2
b = 155
a = 145
Week:
3
b = 157
a = 143
Keeping the end user in mind when writing code is always a good idea. You might
also imagine a need to come back and run your code a year from now. Additionally,
remember that I have eliminated the extra lines MATLAB produces in its output,
so the actual result is actually less pretty than I have shown.
3.2 plotting
plot is a versatile function for plotting points and lines on a two-dimensional
graph. Unfortunately, it is so versatile that it can be hard to use (and hard to read
the documentation!). We will start simple and work our way up.
To plot a single point, type
>> plot(1, 2)
A Figure Window should appear with a graph and a single, blue dot at x position
1 and y position 2. To make the dot more visible, you can specify a different shape:
The letter in single quotes is a string that specifies how the point should be plotted.
You can also specify the color:
r stands for red; the other colors include green, blue, cyan, magenta, yellow and
black. Other shapes include +, *, x, s (for square), d (for diamond), and ^ (for a
triangle).
When you use plot this way, it can only plot one point at a time. If you run
plot again, it clears the figure before making the new plot. The hold command
54 Chapter 3 for loops and basic plotting
lets you override this behavior. hold on tells MATLAB not to clear the figure
when it makes a new plot; hold off returns to the default behavior.
Try this:
>> hold on
>> plot(1, 1, 'o')
>> plot(2, 2, 'o')
When you first execute hold on, a blank Figure Window should appear. Next, the
point (1,1) will be plotted as a circle, and then the point (2,2) will be plotted as
a circle; by default, since no color was specified, the first point (or data set) will
be colored blue, and the second will be colored red. MATLAB scales the plot
automatically; in this example the points are plotted in the corners. You can man-
ually fix the x- and y-axis range using the commands xlim and ylim, respectively.
Keeping Figure Window open, try this:
>> hold on
>> xlim([-1,3])
>> ylim([0,4])
What happened? We just changed the x-axis range to go from –1 to 3, and the
y-axis range to go from 0 to 4.
t We will learn later that the If you close the Figure Window you will need to start over again; you will need
argument provided to xlim to again execute hold on to plot more than one point (or data set). Alternatively,
and ylim is actually a (row)
to begin working with new data, you can keep the Figure Window open and just
vector of length 2.
clear the data using clf; since the Figure Window never closed, there is no need
to re-execute hold on.
In the next example, we will additionally look at adding a figure title, axis
labels, and a legend. As I stated before with for, you should get in the habit of
having a look at the MATLAB documentation pages whenever we learn about a
new MATLAB feature. Here, try doc plot. You will see a range of examples and
learn about other features, such as creating sub-plots.
If this discussion has been unclear, please watch the available screen cast.
Example 3.2
Modify car_loop (Listing 3.1) so that each time through the loop it plots the value
of a versus the value of i (your loop variable).
Once you get that working, modify it so it plots the values of a with red circles and
the values of b with blue diamonds.
One more thing: if you use hold on to prevent MATLAB from clearing the figure,
you might want to clear the figure yourself, from time to time, with the command
clf.
3.2 plotting 55
In the interest of space I again will not display the output from car_update here;
we could instead go back to car_update and suppress printing a and b.
The updated script would be run as:
>> b = 150;
>> a = 150;
>> car_loop2
190
180
170
160
150
140
130
120
110
0 10 20 30 40 50 60
Figure 3.1 The plot generated by MATLAB upon running the script
car_loop2.
Let’s format the image so that it is presentation ready. Keep the Figure Window open
and execute the commands the follow from the command line. Alternatively, you
can add the commands to the end of car_loop2.m and re-run then script (which
in general would be preferred).
First, let’s add labels to the x- and y-axis. This is achieved with the command
xlabel and ylabel, respectively, where we will pass a string which is the axis
label.
>> xlabel('Week')
>> ylabel('Number of Cars')
Since we are simulating the passage of 1 year, or 52 weeks, lets change the x-axis
range to go from 0 to 52:
Next, let’s add a title. We add a title using the title command, where again we
pass a string which is the plot title.
To finish up, let’s add a legend to distinguish between the cars in Albany and
Boston. Each loop iteration, first we plot the number of cars in Albany (a) and
then Boston (b). To add a legend we use the command legend. To legend we will
pass a series of strings, with each string separated by a comma, in the order of the
data sets plotted. Note, there are many subtleties to adding legends to plots with
3.2 plotting 57
multiple data sets, so do not be discouraged if it does not work the first time.
190
Albany
180 Boston
170
Number of Cars
160
150
140
130
120
110
0 10 20 30 40 50
Week
Notice that by default MATLAB places the legend in the top right of the figure,
which covers some of our data. This location is not ideal. You could move it by
clicking on the legend and dragging it to the desired location. In general, I prefer to
do everything from the command line. When I use MATLAB at a high performance
computing center, I do not have access to a graphical interface. Working exclusively
from command line also facilitates moving to a MATLAB alternative such as GNU
Octave. You can move the location of the legend from command line by passing an
optional argument to legend, but I will not do so here to keep from overwhelming
you. (If interested, give help legend or doc legend a try. In fact, every time you
learn a new function, checking out MATLAB’s documentation is always a good
idea.)
Lastly, it would be nice to save a copy of your pretty figure. From the Figure
Window, you can use File Save As... . You can choose the appropriate image for-
mat, and save it to the desired location; your current path is the default location. If
you save your figure as a type “fig” (or a FIG-file to use the MATLAB lingo), it can
be re-opened later in MATLAB and edited. It may be a good idea to save a copy as
a FIG-file and a second copy as an image file you can share and include in your
lab reports. From the command line, saving as FIG-file is accomplished using the
savefig command, and saving as an image file can be accomplished using the
print command. To save the image as FIG-file and a png with name “example_32”
use:
>> savefig('example_32.fig')
58 Chapter 3 for loops and basic plotting
Other formats are available, but again, in the interest of not overwhelming you, we
will stop here. (And again, have a look at MATLAB’s documentation if interested.)
3.3 Sequences
In mathematics a sequence is a set of numbers that corresponds to the positive
integers. The numbers in the sequence are called elements. In math notation,
the elements are denoted with subscripts, so the first element of the series A is
A 1 , followed by A 2 , and so on.
for loops are a natural way to compute the elements of a sequence. As an
example, in a geometric sequence, each element is a constant multiple of the
previous element. As a more specific example, let’s look at the sequence with
A 1 = 1 and the ratio A i +1 = A i /2, for all i . In other words, each element is half as
big as the one before it.
The following loop computes the first 10 elements of A:
a = 1
for i=2:10
a = a/2
end
Each time through the loop, we find the next value of a by dividing the previous
value by 2. Notice that the loop range starts at 2 because the initial value of a
corresponds to A 1 , so the first time through the loop we are computing A 2 .
Each time through the loop, we replace the previous element with the next,
so at the end, a contains the 10th element. The other elements are displayed on
the screen, but they are not saved in a variable. Later, we will see how to save all
of the elements of a sequence in a vector.
This loop computes the sequence recurrently, which means that each el-
ement depends on the previous one. For this sequence it is also possible to
compute the i th element directly, as a function of i , without using the previous
¡ ¢i −1
element. In math notation, A i = A 1 12 .
Example 3.3
Write a script named sequence that uses a loop to compute elements of A directly.
3.4 Series
In mathematics, a series is the sum of the elements of a sequence. It’s a terrible
name, because in common English, “sequence” and “series” mean pretty much
the same thing, but in math, a sequence is a set of numbers, and a series is an
expression (a sum) that has a single value. In math notation, a series is often
P
written using the summation symbol .
For example, the sum of the first 10 elements of A is
10
X
Ai
i =1
¡ 1 ¢i −1
where A i = A 1 2 . A for loop is a natural way to compute the value of this
series:
a1 = 1;
total = 0;
for i=1:10
a = a1 * 0.5^(i-1);
total = total + a;
end
ans = total
60 Chapter 3 for loops and basic plotting
a1 is the first element of the sequence. Each time through the loop a is the i th
element. Note we can start with i=1 because we are computing the elements
directly, and the first time through the loop, i=1 resulting in a=a1. Had we been
calculating recurrently, we would need to specify a starting point, as we will see
in the next Example.
t When using a for loop to The way we are using total is sometimes called an accumulator; that is, a
compute a sum, the accu- variable that accumulates a result a little bit at a time. Before the loop we initialize
mulator will almost always
it to 0. Each time through the loop we add in the i th element. At the end of the
start with a value of 0. This
way when we add our first
loop, total contains the sum of the elements. Since that’s the value we were
term to the sum, the sum is looking for, we assign it to ans.
equal to the first term. Like-
wise, when we later use for
loops to compute continu- Example 3.4
ous products, we will start In this section, we wrote a for loop to compute the sum of the sequence (or series),
with a value of 1 where each element was computed directly. Write a script named series_direct
to perform the calculation. Next, write a script named series_recurrent to
compute the same sum with the elements computed recurrently. You will have to
be careful about where you start and stop the loop.
(ans = 1.9980)
When executed, both scripts properly compute ans = 1.9980. Note that in
series_direct, we could start with i=2, but then total needs to begin with
(or be initialized with) total=a1.
3.5 Generalization
As written, the previous example always adds up the first 10 elements of the
sequence, but we might be curious to know what happens to total as we increase
the number of terms in the series. If you have studied geometric series, you might
know that this series converges on 2; that is, as the number of terms goes to
infinity, the sum approaches 2 asymptotically.
62 Chapter 3 for loops and basic plotting
To see if that’s true for our program, series_direct, we could replace the
constant, 10, with a variable named n:
a1 = 1;
total = 0;
for i=1:n
a = a1 * 0.5^(i-1);
total = total + a;
end
ans = total
Now the script can compute any number of terms, with the precondition that
you have to set n before you execute the script. Here’s how you could run it with
different values of n, where I use format long to display more decimal places:
t We haven’t previously seen
multiple commands on >> format long
a single line like we have
>> n=10; series_direct
here. Multiple commands
separated by semi-colon
total = 1.99804687500000
can be placed on a single
line rather than a separate >> n=20; series_direct
line for each command. In total = 1.99999809265137
series_direct the final
sum in printed to screen; >> n=30; series_direct
this therefore must end a
total = 1.99999999813735
line.
3.6 Examples
Practice makes perfect...
Example 3.5
Write a general script that uses a for loop to compute the factorial of an arbitrary
integer n. Test you script by computing the factorial of a number and comparing
to the result of the MATLAB function factorial.
The recall factorial of 3 is:
3! = 3 · 2 · 1
or equivalently
3! = 1 · 2 · 3
I write this second expression as a hint as to how you might set-up a for loop. In
general,
n! = n · (n − 1) · (n − 2) · ... · 1
or
n! = Πni=1 i
Note that I could start my loop with either i=1 or i=2 and obtain the same result.
When computing sums with a for loop, we typically initialize the counter variable
with a value of zero. A number plus zero is equal to that number. With continuous
products, we typically initialize the counter variable with a value of one. A number
times one is equal to that number.
64 Chapter 3 for loops and basic plotting
Example 3.6
During class, we found that sin(pi) was not exactly equal to zero as we expected.
The reason for this is pi is not exact but is rather a numerical approximation. It is
valuable to know how MATLAB actually computes sine, and how the numerical
error is thus propagated through the calculation.
The calculation of sine is not a standard operator. The MATLAB function is actually
performing a Taylor series expansion about the specified point:
∞ x 2i +1
(−1)i
X
sin (x) = (3.1)
i =0 (2i + 1)!
MATLAB can not carry out the summation to ∞, so it must truncate the series. This
introduces additional numerical error in the calculation, although the number of
terms used is large enough that you likely do not notice. This is in addition to the
numerical error of approximating pi, which is carried throughout the sum.
Write a script to compute sin(pi) using a Taylor series expansion, and compare
to the MATLAB function sin. Run your script multiple times to see how the result
changes with the number of terms in the series. Note that eq. (3.1) requires the
calculation of the factorial of 2i + 1. You can add a second, “nested” loop to your
script to compute the factorial, just be sure to use a unique loop variable.
28 for j=2:dum
29 n_fact = n_fact*j;
30 end
31 % Calculating the ith Taylor term
32 taylor_term = (-1)^(i)*x^(dum)/n_fact;
33 % Updating my Talylor sum
34 taylor_ans = taylor_ans+taylor_term;
35 plot(i,taylor_ans,'ro')
36 end
37 % Labeling the plot axis
38 xlabel('n')
39 ylabel('sin(x)')
40
41 taylor_ans
42 ans_matlab = sin(x)
1.6
1.5
1.4
1.3
sin(x)
1.2
1.1
0.9
0 5 10 15 20
n
Example 3.7
We have already seen the Fibonacci sequence, F , which is defined recurrently as
F i = F i −1 + F i −2
In order to get started, you have to specify the first two elements, but once you
have those, you can compute the rest. The most common Fibonacci sequence
starts with F 1 = 1 and F 2 = 1. Write a script that uses a for loop to compute the
first 10 elements of this Fibonacci sequence. As a postcondition, your script should
assign the 10th element to ans.
Now generalize your script so that it computes the n th element for any value of
n, with the precondition that you have to set n before you run the script. To keep
things simple for now, you can assume that n is greater than 2.
Hint: you will have to use two variables to keep track of the previous two elements
of the sequence. You might want to call them prev1 and prev2. Initially, prev1
= F1 and prev2 = F2 . At the end of the loop, you will have to update prev1 and
prev2; think carefully about the order of the updates!
(ans = 55)
Example 3.8
Write a script that loops i through a range from 1 to 20, uses the script from
Example 3.7 to compute Fibonacci numbers, and plots F i for each i with a series
of red circles.
3.6 Examples 67
7000
6000
5000
4000
Fi
3000
2000
1000
0
0 5 10 15 20
i
Example 3.9
Let’s build-upon the script you wrote for Example 2.4 on page 38. Recall you
wrote a script to calculate the vapor pressure (p sat ) of ethanol for a given T , where,
A = 8.13484, B = 1662.48, and C = 238.131, and is valid over the range −114.1 ◦ C <
T < 243.1◦ C, where T is in ◦ C and p sat is in mmHg.
Here, let’s create a plot of our results. A common way to represent the pT plane is
using a Clapeyron plot. In a Clapeyron plot, we plot log10 p sat or ln p sat versus 1/T .
Since it is inverse T , T should be in absolute units (e.g., K). Create a Clapeyron plot
for ethanol over the range 0 to 100 ◦ C, where p sat is in units of mmHg and T is in
units of K. Represent each point with a circle. Label your axis and give your plot a
title.
28
29 % labeling the axis
30 xlabel('1/T [1/K]')
31 ylabel('log10 p/mmHg')
32
33 % plot title
34 title('Clapeyron plot for ethanol for 0 to 100 C')
35
36 % Last, let's get in the habit of saving and printing our graphs
37 savefig('ethanol_clapeyron_plot.fig')
38 print('-dpng', 'ethanol_clapeyron_plot')
>> a = 8.13484;
>> b = 1662.48;
>> c = 238.131;
>> clapeyron_plot
3
log10 p/mmHg
2.5
1.5
1
2.6 2.8 3 3.2 3.4 3.6 3.8
1/T [1/K] ×10 -3
Figure 3.5 The plot generated by MATLAB upon running the script
clapeyron_plot.
Note, here I require a, b, and c as pre-conditions, despite indicating throughout
the script that it is for ethanol. It would in general be better to assign values to a, b,
and c within the script. This would give me a record of exactly what was done, in
addition to being inline with our ethanol specific script. It would then be easy to
update the script for a new compound and just rename the file. However, I did not
do this to give us practice specifying pre-conditions.
3.7 CPB Examples 71
Example 3.10
Let’s build upon the script you wrote for Example 2.5 on page 41. Recall you wrote
a script to calculate the average speed of a molecule at a specified temperature.
You used your script to compare the average speed of water and ethanol at 0 ◦ C.
Create a plot of the average speed of water and ethanol over the range 0 to 100 ◦ C.
Use a different symbol to represent water and ethanol. Label your axis, give your
plot a title, and add a legend.
Adding a legend can be tricky. Things will become much easier when we learn
about vectors, but we are not there yet. Every time you plot a new point, MATLAB
treats this as a new set of data. The legend labels need to be provided in the same
order as the data sets that have been plotted. So if you plot a point for water first
and then ethanol, you are all set. But if you plot all of the water data and then all of
the ethanol data, it will not work as expected. Why, because the first two sets of
data correspond to the first two water points. Give it a try both ways and you will
see what I mean.
>> maxwell_plot
650
600
speed [m/s]
550
500
450
400
350
0 20 40 60 80 100
T [C]
Figure 3.6 The plot generated by MATLAB upon running the script
maxwell_plot.
3.8 Creating multiple figures 73
>> maxwell_multi_plot
3.8 Creating multiple figures 75
700
680
speed [m/s]
660
640
620
600
0 20 40 60 80 100
T [C]
Figure 3.7 The first figure for water generated by MATLAB upon running
the script maxwell_multi_plot.
440
430
speed [m/s]
420
410
400
390
380
0 20 40 60 80 100
T [C]
Figure 3.8 The second figure for ethanol generated by MATLAB upon run-
ning the script maxwell_multi_plot.
Additionally, I would encourage you to have a look at help figure and doc
figure. You may also be interested in MATLAB’s subplot, which can create
multiple plots within the same figure window. This can be useful to facilitate
76 Chapter 3 for loops and basic plotting
3.9 Glossary
loop: A part of a program that runs repeatedly.
loop variable: A variable, defined in a for statement, that gets assigned a differ-
ent value each time through the loop.
range: The set of values assigned to the loop variable, often specified with the
colon operator; for example 1:5.
body: The statements inside the for loop that are run repeatedly.
3.10 Exercises
Exercise 3.1 Let’s build-upon the script you wrote for Exercise 2.1 on page 45. Recall you
wrote a script to calculate the surface tension (σ) at saturation for ethanol, propane, and
acetone at a given temperature (T ). We looked at a range of temperatures to understand
if σ increased or decreased with increasing temperature, and we checked to see that σ
approached a value of zero at the critical point.
Here, let’s build off of this. The script we wrote for Exercise 2.1 works such that if we
specify T , it returns the value of σ. This script works and is bug free, so in the spirit of unit
testing and incremental development, let’s keep it relatively untouched and build-off of
it.
a) Create a new script that loops over the entire temperature range of applicability,
computes σ, and then plots σ versus T . Plot the results for both ethanol, propane, and
acetone in the same figure. Be sure to use a different symbol and color for ethanol,
propane, and acetone, add a title, axis labels, and a legend. (While not required for
homework, try and create three separate figures too.) Do your observations agree with
your findings in Exercise 2.1?
b) It can be difficult to compare ethanol, propane, and acetone in the same figure because
their temperature ranges are so different. One strategy to facilitate comparison is to
plot with respect to the reduced temperature (Tr ):
T
Tr = (3.2)
Tc
where Tc is the critical temperature. Create a copy of your script and update to plot σ
versus Tr , then re-run. Note that plotting in reduced units is also a common practice
as the properties commonly exhibit (near) universal behavior. This is the basis of
corresponding states theory.
In case there is an issue looking back at Exercise 2.1, the expression for σ along with
the parameters are re-produced below. 1 The value of Tmax is Tc .
σ = A + BT +C T 2 + DT 3 + E T 4 + F T 5
Where σ is the surface tension in units of N/m, T is the temperature in K, and A, B , C , D,
E , and F are regressed coefficients.
1 Yaws, Carl L.. (2012; 2013; 2014). Yaws’ Critical Property Data for Chemical Engineers
and Chemists. Knovel. Retrieved from https://app.knovel.com/hotlink/toc/id:kpYCPDCECD/
yaws-critical-property/yaws-critical-property
78 Chapter 3 for loops and basic plotting
Exercise 3.2 Let’s build upon Exercise 2.2. “Yaws’ Critical Property Data for Chemical
Engineers and Chemists” recommends use of a modified Watson equation to calculate
the enthalpy of vaporization as a function of temperature:
T n
µ ¶
∆H vap = A 1 − (3.3)
B
where A, B and n are regressed coefficients, T is the temperature in K, and ∆H vap is
the enthalpy of vaporization in units of kJ/mol. Below I have expanded the table of
parameters from Exercise 2.2 for the homologous series of linear alkanes. (Specifically, I
have eliminated ethanol and added n-octane.)
Write a script to compute ∆H vap for each compound at 300 K, and create a plot of
vap
∆H versus carbon number. Add a title, label your axis, and print the resulting plot to
file. Does ∆H vap appear to increase linearly with the carbon number?
Exercise 3.3 Let’s build upon Exercise 2.3. “Yaws’ Handbook of Properties of Aqueous
Systems”2 recommends the following equation to compute the solubility of organic gases
in water:
B
log10 S = A + +C log10 T (3.4)
T
where S is the solubility in water in parts per million by weight (ppm), T is the temperature
in K, and A, B , and C are regressed coefficients.
Write a script to compute log10 S for methane over the entire temperature range of
applicability (Tmin to Tmax ), and plot log10 S versus temperature. Add a title and axis
labels. Also, have your script save and print your figure.
2 Yaws, Carl L. (2012). Yaws’ Handbook of Properties of Aqueous Systems. Knovel. Re-
Exercise 3.4 Let’s build upon Exercise 2.4. Build upon your script to plot f versus Re
for a given value of ²/D. That is, make your own Moody chart. Cool! For the purpose of
this question, write a script where you loop over Re values over the range of 1 × 103 to
1.1 × 103 , for each Re compute f for the case of ²/D = 0.1 and ²/D = 0.001, and plot. Plot
the results for ²/D = 0.1 and ²/D = 0.001 on a separate plots, and be sure to add add a
title and axis labels.
Note that the results will not be too interesting. To make a quality Moody diagram we
need more data points over a wider range of Re values, and we should use a log-scale. No
worries, we will build up to this in future chapters.
Chapter 4
Logic and vectors
• Apply the use of logical blocks and vectors to solve basic engineering prob-
lems
If you work through the chapter and believe these goals are not met, please
re-review the material and reach out for help.
total = 0
Why? Because the expression 1:-1 means “all the numbers from 1 to –1, counting
up by 1.” It’s not immediately obvious what that should mean, but MATLAB’s
interpretation is that there aren’t any numbers that fit that description, so the
result is
>> 1:-1
ans = Empty matrix: 1-by-0
If the matrix is empty, you might expect it to be “0-by-0,” but there you have it. In
any case, if you loop over an empty range, the loop never runs at all, which is why
in this example the value of total is zero for any negative value of n.
If you are sure that you will never make a mistake, and that the preconditions
of your functions will always be satisfied, then you don’t have to check. But for
the rest of us, it is dangerous to write a script, like this one, that quietly produces
the wrong answer (or at least a meaningless answer) if the input value is negative.
A better alternative is to use an if statement.
4.2 if
The if statement allows you to check for certain conditions and execute state-
ments if the conditions are met. In the previous example, we could write:
t Remember, MATLAB’s doc-
umentation is your friend. if n<0
Try help if and doc if.
ans = NaN
You can also do the same
for else and elseif.
end
The syntax is similar to a for loop. The first line specifies the condition we are
t While I will not use them
in the current version of interested in; in this case we are asking if n is negative. If it is, MATLAB executes
the text, know that MAT- the body of the statement, which is the indented sequence of statements between
LAB does have an error the if and the end. MATLAB doesn’t require you to indent the body of an if
function that can be used statement, but it makes your code more readable, so you should do it, and don’t
to throw an error and dis- make me tell you again.
play a message, and also a In this example, the “right” thing to do if n is negative is to set ans = NaN,
warning function that can
which is a standard way to indicate that the result is undefined (not a number).
be used to display warning
messages. If the condition is not satisfied, the statements in the body are not executed;
put differently, if the condition is not met, no action is taken and the program
proceeds. Sometimes there are alternative statements to execute when the con-
dition is false. In that case you can extend the if statement with an else clause.
The complete version of the previous example might look like this:
4.2 if 83
if n<0
ans = NaN
else
a1 = 1;
total = 0;
for i=1:n
a = a1 * 0.5^(i-1);
total = total + a;
end
ans = total
end
Statements like if and for that contain other statements are called compound
statements. All compound statements end with, well, end. Take note of my use of
indentation. It is meant to make the code more readable.
In this example, one of the statements in the else clause is a for loop. Putting
one compound statement inside another is legal and common, and sometimes
called nesting.
To be completely clear as to what MATLAB is doing here, let’s walk through
the flow of calculations. First, MATLAB checks whether n is less than 0. If it is, the
if statement is true, and MATLAB sets ans = NaN and proceeds directly to the
end statement nicely lined up with if and else. If n is equal to or greater than 0,
the if statement is false, and MATLAB therefore proceeds to the else statement,
and performs the calculations between else and the final end.
Getting back to the use of indentation: if, else, and end make up a com-
pound statement, and are all therefore aligned. We start with the if statement,
and if it is false, we jump to else. They are aligned to make this clear. The other
lines are indented to show they are part of this compound statement. With the
if statement, we “open” our (outer) compound statement. With the for, we
open a second, nested (inner) compound statement. We must “close” the for
loop (or the inner statement) before we close the (outer) if compound statement.
Therefore the first end statement ends (or closes) the for loop since it must be
closed first. To make this clear, we line them up.
I hope my long-winded discussion in the last two paragraphs has not confused
you more. As we begin to look at examples and you begin practicing, it will all
come together. Lastly, remember the else statement essentially means for all
other cases. You can add additional cases beside that in the original if statement.
This would be accomplished with an elseif statement, where you can have as
many elseif statements as you would like. An example of this might be:
84 Chapter 4 Logic and vectors
>> if x=5
if x=5
|
Error: The expression to the left of the equals sign is not a
valid target for an assignment.
>> x = 5;
>> 0 < x < 10 % right for the wrong reason
ans = 1
>> x = 17
>> 0 < x < 10 % just plain wrong
ans = 1
The problem is that MATLAB is evaluating the operators from left to right, so
first it checks if 0<x. It is, so the result is 1. Then it compares the logical value 1
(not the value of x) to 10. Since 1<10, the result is true, even though x is not in the
interval. For beginning programmers, this is an evil, evil bug!
One way around this problem is to use a nested if statement to check the
two conditions separately:
ans = 0
if 0<x
if x<10
ans = 1
end
end
ans = 0
if 0>=x
% I will provide no statement which means
% do nothing if this is true, proceed to end.
elseif x<10
% We only get to this elseif statement if x>0,
% for which the first if statement is false.
% If we find x<10, this is the same as
% 0<x<10
ans = 1
end
But it is more concise to use the AND operator, &&, to combine the conditions.
>> x = 5;
>> 0<x && x<10
ans = 1
>> x = 17;
>> 0<x && x<10
ans = 0
The result of AND is true if both of the operands are true. The OR operator, ||, is
true if either or both of the operands are true. Put differently, the OR operator is
true is at least one of operands is true.
You can have as many AND and/or OR operators you would like on a single
line. Comparisons will be made from left to right, unless parentheses are added.
4.5 exist
Let’s return to section 4.1 Checking preconditions. Our discussion so far has
been limited to checking whether or not a variable (a pre-condition) is properly
defined. For example, if I am solving a problem using the Antoine equation, is the
specified temperature within the range of applicability. It may also be desirable
to check to see if a variable (required as a pre-condition) is defined at all. Let’s
look at a basic example. I wish to compute ni=1 (i + 1)2 , where n is the number of
P
Next, let’s clear the variable n and see what would happen if we forget to
define it.
>> clear n;
>> sum_test
total = 0
The script returns the value of total = 0, which is the value we initialize it to,
and then also an error message indicating that n is undefined along with the line
on which the error was encountered. This is a good error message. However, if it
is missed by the user for one reason or another, they have total = 0 which may
be used in subsequent calculations.
You may therefore wish to check to see if n has been defined. If it has, run
the code as normal, if not, return an answer such as total = NaN and possibly
an error message of your own. This can be accomplished with the exist func-
tion. There are two ways to call it, I prefer the natural function form so that I
remember it is a function. When used to check for the existence of a variable, it
can be thought of as a logical function, returning a value of 1 for true, the variable
exists, and a value of 0 for false, the variable does not exist. It takes the basic form
exist(’name’,’var’), where name is the name of the variable you are checking
exists, and var indicates that we are checking for the existence of a variable. You
can check for the existence of things besides variables, but you can read the docu-
mentation for that. Let’s try and integrate exist into our script.
88 Chapter 4 Logic and vectors
Remember, another issue with scripts is they can read variables from and
save variables to your workspace. What would be unfortunate is if total was a
variable already defined in your workspace that you did not want to overwrite.
Exist could be used to check if total is already defined. If it is, then you could
save the current value to a new variable, or maybe you print a warning, or maybe
you prevent the script from running all together. The choice is up to you. The last
note I will make, if you need to check for the existence of more than one variable,
then you can combine exist with the AND operator, &&.
4.6 Vectors
The values we have seen so far are all single numbers, which are called scalars
to contrast them with vectors and matrices, which are collections of numbers.
In this chapter we will focus on the basics, in the context of loops. Later we will
revisit the topic in greater detail.
A vector in MATLAB is similar to a sequence in mathematics; it is a set of
numbers that correspond to positive integers. What we called a “range” in the
previous chapter was actually a vector.
In general, anything you can do with a scalar, you can also do with a vector.
You can assign a vector value to a variable:
4.7 Vector arithmetic 89
>> X = 1:5
X = 1 2 3 4 5
Variables that contain vectors are often capital letters. That’s just a convention;
MATLAB doesn’t require it, but for beginning programmers it is a useful way to t This gives us a deeper un-
remember what is a scalar and what is a vector. derstanding of how a for
loop works. MATLAB keeps
Just as with sequences, the numbers that make up the vector are called ele-
track of the loop iteration
ments. number. Each iteration the
loop index variable is set
equal to that corresponding
4.7 Vector arithmetic element of the specified
vector.
You can perform arithmetic with vectors, too. If you add a scalar to a vector,
MATLAB increments each element of the vector:
>> Y = X+5
Y = 6 7 8 9 10
>> Z = X+Y
Z = 7 9 11 13 15
But adding vectors only works if the operands are the same size. Otherwise:
>> W = 1:3
W = 1 2 3
>> X+W
Error using +
Matrix dimensions must agree.
The error message in this case is confusing, because we are thinking of these
values as vectors, not matrices. The problem is a slight mismatch between math
vocabulary and MATLAB vocabulary.
You can see this if you use the whos command to display the variables in the
workspace. whos is similar to who except that it also displays the size and type of
each variable.
First I’ll make one of each kind of value:
>> scalar = 5
scalar = 5
t To get a vector of 1’s, try ones is a function that builds a new matrix with the given number of rows (2) and
ones(1,3). MATLAB can columns (3), and sets all the elements to 1. Now let’s see what we’ve got.
also created a new matrix of
0’s, Try zeros(2,3). Both >> whos
are useful for creating a Name Size Bytes Class Attributes
new matrix of a given di-
mension and initializing it.
What is the result if you try scalar 1x1 8 double
ones(3)? A 3 by 3 “square” vector 1x5 40 double
matrix. Have a look at the matrix 2x3 32 double
documentation with the
help command. According to MATLAB, everything is a double array: “double” is another name
for double-precision floating-point numbers, and “array” is another name for a
matrix.
The only difference is the size, which is specified by the number of rows and
columns. The thing we called scalar is, according to MATLAB, a matrix with one
row and one column. Our vector is really a matrix with one row and 5 columns.
And, of course, matrix is a matrix, with 2 rows and 3 columns.
The point of all this is that you can think of your values as scalars, vectors, and
matrices, and I think you should, as long as you remember that MATLAB thinks
everything is a matrix.
Here’s another example where the error message only makes sense if you
know what is happening under the hood:
>> X = 1:5
X = 1 2 3 4 5
>> Y = 1:5
Y = 1 2 3 4 5
4.9 Indices 91
>> Z = X*Y
Error using *
Inner matrix dimensions must agree.
First of all, * is the MATLAB function that performs matrix multiplication. The
reason the “inner matrix dimensions must agree” is the way matrix multiplication
is defined in linear algebra. Any scalar (a 1-by-1 matrix) may multiply anything.
Otherwise, the number of columns in X has to equal the number of rows in Y
(those are the inner dimensions).
If you don’t know linear algebra, this doesn’t make much sense. When you
saw X*Y you probably expected it to multiply each of the elements of X by the
corresponding element of Y and put the results into a new vector. That operation
is called elementwise multiplication, and the operator that performs it is .*:
>> X .* Y
ans = 1 4 9 16 25
We’ll get back to the elementwise operators later; you can forget about them for t As we will see, we also have
now. elementwise division (./)
and elementwise exponenti-
ation (.ˆ).
4.9 Indices
t So far we have only used the
You can select elements of a vector with parentheses:
ones command to create
a matrix, which is not too
>> Y = 6:10 interesting. But to select
Y = 6 7 8 9 10 an element of a matrix,
we would have something
>> Y(1) like this: Y(3,4). The first
ans = 6 number is the row index,
and the second number is
the column index.
>> Y(5)
ans = 10
This means that the first element of Y is 6 and the fifth element is 10. The number
in parentheses is called the index because it indicates which element of the vector
you want.
The index can be any kind of expression.
>> i = 1;
>> Y(i+1)
ans = 7
92 Chapter 4 Logic and vectors
Loops and vectors go together like the storm and rain. For example, this loop
displays the elements of Y.
for i=1:5
Y(i)
end
Each time through the loop we use a different value of i as an index into Y.
A limitation of this example is that we had to know the number of elements in
Y. We can make it more general by using the length function, which returns the
number of elements in a vector:
t length can also be used
with a matrix to find the for i=1:length(Y)
number of elements in a
Y(i)
dimension (along a row
or column). You can also
end
ask for the length of a sub-
set of a vector or matrix There. Now that will work for a vector of any length.
dimension. But more on
this later... or have a look at
the documentation if you 4.10 Indexing errors
are eager. And if you are
really eager, when working An index can be any kind of expression, but the value of the expression has to be
with matrices the related a positive integer, and it has to be less than or equal to the length of the vector. If
function size can be very it’s zero or negative, you get this:
useful.
>> Y(0)
Subscript indices must either be real positive integers or logicals.
“Subscript indices” is MATLAB’s longfangled way to say “indices.” “Real positive in-
tegers” means that complex numbers are out. And you can forget about “logicals”
for now; we will certainly come back to logicals later.
If the index is too big, you get this:
>> Y(6)
Index exceeds matrix dimensions.
There’s the “m” word again, but other than that, this message is pretty clear.
Finally, don’t forget that the index has to be an integer:
>> Y(1.5)
Subscript indices must either be real positive integers or logicals.
4.11 Vectors and sequences 93
Notice that I am using a capital letter for the vector F and lower-case letters for the
scalars i and n. At the end, the script extracts the final element of F and stores it
in ans, since the result of this script is supposed to be the n th Fibonacci number,
not the whole sequence.
If you had any trouble with Example 3.7, you have to appreciate the simplicity
of this version. The MATLAB syntax is similar to the math notation, which makes
it easier to check correctness. The only drawbacks are
• You have to be careful with the range of the loop. In this version, the loop
runs from 3 to n, and each time we assign a value to the ith element. It
would also work to “shift” the index over by two, running the loop from 1 to
n-2:
94 Chapter 4 Logic and vectors
Either version is fine, but you have to choose one approach and be consis-
tent. If you combine elements of both, you will get confused. I prefer the
version that has F(i) on the left side of the assignment, so that each time
through the loop it assigns the ith element.
• If you really only want the nth Fibonacci number, then storing the whole
sequence wastes some storage space. But if wasting space makes your code
easier to write and debug, that’s probably okay.
Example 4.1
Write a loop that computes the first n elements of the geometric sequence A i +1 =
A i /2 with A 1 = 1. Notice that the math notation puts A i +1 on the left side of the
equality. When you translate to MATLAB, you may want to shift the index. Note:
We are only asked to compute the terms of the series and not the sum as we did in
Example 3.4.
4.11 Vectors and sequences 95
Solution: (Link to screen cast and accompanying M-file.) I will provide two ex-
amples. First, I will shift the index as suggested. To me, this makes more sense
logically. Second, I will compute the terms using the notation as written.
>> T(1) = 1
T = 1
>> T(2) = 12
T = 1 12
>> T(3) = 5
T = 1 12 5
>> T(8) = 1
T = 1 12 5 0 0 0 0 1
The length of the vector is increased from 3 to 8, and the value of elements 4 to 7,
which we “skipped”, are assigned a value of 0.
Likewise, if we were to move next to the element in the 8th row and 12th col-
umn, all of the elements that we “skipped” will be assigned a value of zero:
>> T(8,12) = 1
T = 1 12 5 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1
Having dynamic vectors and matrices is convenient and may simplify your code.
However, you may find that the MATLAB editor suggests that you pre-allocate
your vectors and matrices when possible. The issue is that every time you re-size
your vector or matrix, MATLAB needs to reassign memory. This can be a time
consuming operation. For the purpose of this class, you could safely ignore the
suggestion without any implications. However, if you know the final size of your
4.13 Creating vectors 97
matrix or length of your vector, a better practice would be to initialize (or create)
the matrix or vector to be the final size using zeros or ones. This way memory
never needs to be reassigned. For example, if we knew the final length of vector G
would be 10, we might initially define G as:
>> G = zeros(1,10)
G = 0 0 0 0 0 0 0 0 0 0
For this class, you will not notice a change in efficiency. However, if you know
the final size of your matrix or length of your vector, then initializing your matrix
or vector to be the final size is the better practice. If you are interested in high
performance computing, then getting in the habit of sizing your vectors and
matrices up-front is a good idea. For these applications any time savings can have
a huge impact.
>> 1:5
ans =
1 2 3 4 5
>> 1.1:5.8
ans =
1.1000 2.1000 3.1000 4.1000 5.1000
>> 5:1
ans =
1x0 empty double row vector
Beside the colon operator, we have the extended colon operator. The extended
colon operator takes the form begin:increment:end, where here we can specify
an increment value (beside the default value of 1).
98 Chapter 4 Logic and vectors
>> 1:2:6
ans =
1 3 5
>> 1:1.5:6
ans =
1.0000 2.5000 4.0000 5.5000
>> 6:-1:1
ans =
6 5 4 3 2 1
>> ones(1,5)
ans =
1 1 1 1 1
>> zeros(1,5)
ans =
0 0 0 0 0
>> T(1) = 5
T =
5
>> T(2) = 3
T =
5 3
>> T(3) = 10
T =
5 3 10
If our goal was to create a vector with data that we know, we can accomplish
this in a single command using brackets, with the elements separated either by a
space or a comma.
4.13 Creating vectors 99
>> R = [5 3 0]
R =
5 3 0
>> S = [5, 3, 0]
S =
5 3 0
For the stubborn Excel user reluctant to give MATLAB a try, you could likewise
accomplish this graphically using the New Variable button on the Home tab. You
Figure 4.1 The New Variable and
can likewise edit an existing variable using the Open Variable button, or just dou- Open Variable buttons located on
ble click the variable name in the Workspace . To create a new variable, simply the Home tab.
click New Variable , then type into a spreadsheet-like environment. To give the
variable a name, click on the tab that currently says “unnamed”, and type in a
variable name of your choice. This is best demonstrated in the following screen
cast.
Another useful command is linspace. I often find students using the func-
tion in class without ever mentioning it. The basic command is linspace(begin,end)
which will create a vector of 100 equally spaced points between begin and end. If
you want to use more or less than 100 points, you can use linspace(begin,end,n)
where n is the number of points. This can be very useful for the examples where
we have looped over a range of temperatures. Here the endpoints need not be in-
tegers. If we were to create a vector of temperatures using linspace and assigned
it to variable T, we could then loop as for i=1:length(T), and each iteration
point to the i th element of T.
And very last, we will end with the command rand. The basic form of the
command is rand(1,n), which will create a vector of length n of uniformly dis-
tributed pseudorandom numbers between 0 an 1. Technically the numbers are
pseudorandom as they are generated using a definite mathematical procedure,
but for our purposes you can think of them as random. It takes the same form as
ones and zeros, where the first argument is actually the number of rows. This
will get much more attention later.
>> Y=rand(1,3)
Y =
0.8147 0.9058 0.1270
Again, we will not use most of these commands for some time yet. However,
there is always a curiosity when we begin to discuss vectors, so here it is. When
100 Chapter 4 Logic and vectors
we use the command in the text, we will give it a more proper introduction.
But hopefully this satisfies your curiosity. Now back to our originally scheduled
programming.
>> n = 10;
>> fibonacci_seq_script
ans = 55
>> plot(F)
>> xlabel('n')
>> ylabel('F_n')
60
50
40
Fn
30
20
10
0
1 2 3 4 5 6 7 8 9 10
n
Figure 4.2 A plot of the first ten elements of the Fibonacci sequence.
This display is often useful for debugging, especially if your vectors are big enough
that displaying the elements on the screen is unwieldy.
If you call plot with two vectors as arguments, MATLAB plots the second one
as a function of the first; that is, it treats the first vector as a sequence of x values
and the second as corresponding y value and plots a sequence of (x, y) points.
4.14 Plotting vectors 101
>> X = 1:5;
>> Y = 6:10;
>> plot(X, Y)
>> xlabel('X')
>> ylabel('Y')
10
9.5
8.5
Y
7.5
6.5
6
1 1.5 2 2.5 3 3.5 4 4.5 5
X
Figure 4.3 An example of plotting two vectors, one against the other.
By default, MATLAB draws a blue line, but you can override that setting with the
same kind of string we saw in Section 3.2. For example, the string ’ro-’ tells
MATLAB to plot a red circle at each data point; the hyphen means the points
should be connected with a solid line. Rather than a solid line, we can obtain a
dashed line with ’--’ and a dotted line with ’:’.
In this example, I stuck with the convention of naming the first argument X t When overriding the default
(since it is plotted on the x-axis) and the second Y. There is nothing special about settings and specify the line
and/or symbol type and
these names; you could just as well plot X as a function of Y. MATLAB always treats
the color, MATLAB does not
the first vector as the “independent” variable, and the second as the “dependent” care about the order. Try for
variable (if those terms are familiar to you). example ’ro-’, ’r-o’, and
I should also point out that despite plotting multiple data points, five in this ’-or’. You should find they
case, I did not need to use the hold on command. The reason is that MATLAB all yield the same result.
treats this a single set. If we wanted to add any additional points to the plot using
a subsequent plot command, then we would need to use hold on. Of course I
could have just used hold on from the start and nothing would have changed.
102 Chapter 4 Logic and vectors
4.15 Reduce
A frequent use of loops is to run through the elements of an array and add them
up, or multiply them together, or compute the sum of their squares, etc. This kind
of operation is called reduce, because it reduces a vector with multiple elements
down to a single scalar.
For example, this loop adds up the elements of a vector named X (which we
assume has been defined).
total = 0
for i=1:length(X)
total = total + X(i)
end
ans = total
The use of total as an accumulator is similar to what we saw in Section 3.4. Again,
we use the length function to find the upper bound of the range, so this loop will
work regardless of the length of X. Each time through the loop, we add in the ith
element of X, so at the end of the loop total contains the sum of the elements.
This is additionally an excellent example to practice with because MATLAB has
a built-in function sum that does the same thing. You could use it to check the
script for correctness as sum(X).
Example 4.2
Write a similar loop that multiplies all the elements of a vector together. You might
want to call the accumulator total_prod, and you might want to think about the
initial value you give it before the loop.
Solution: (Link to screen cast and accompanying M-file.) Careful here. Do not
initialize your accumulator variable to a value of zero. Anything times zero is
zero. This is fine when performing a summation, but not a continuous product.
Initialize it to a value of one.
4.16 Apply 103
4.16 Apply
Another common use of a loop is to run through the elements of a vector,
perform some operation on the elements, and create a new vector with the results. t Remember that X(i) refers
This kind of operation is called apply, because you apply the operation to each to a single element of the
vector X. We can therefore
element in the vector.
use ˆ and do not need .ˆ,
For example, the following loop computes a vector Y that contains the squares
although both would give
of the elements of X (assuming, again, that X is already defined). the same result.
4.17 Search
Yet another use of loops is to search the elements of a vector and return the index
of the value you are looking for (or the first value that has a particular property).
For example, if a vector contains the computed altitude of a falling object, you
might want to know the index where the object touches down (assuming that the
ground is at altitude 0).
To create some fake data, we’ll use an extended version of the colon operator:
4.17 Search 105
X = 10:-1:-10
The values in this range run from 10 to –10, with a step size of -1. The step size is
the interval between elements of the range.
The following loop finds the index of the element 0 in X: t Try writing a for loop with
the extended version of the
colon operator. This gives
for i=1:length(X)
you the ability to write a
if X(i) == 0 for loop that increments
ans = i by a value other than 1 each
end iteration.
end
One funny thing about this loop is that it keeps going after it finds what it is
looking for. That might be what you want; if the target value appears more than
once, this loop provides the index of the last one.
But if you want the index of the first one (or you know that there is only one),
you can save some unnecessary looping by using the break statement.
for i=1:length(X)
if X(i) == 0
ans = i
break
end
end
break does pretty much what it sounds like. It ends the loop and proceeds
immediately to the next statement after the loop (in this case, there isn’t one, so
the script ends).
This example demonstrates the basic idea of a search, but it also demonstrates
a dangerous use of the if statement. Remember that floating-point values are
often only approximately right. That means that if you look for a perfect match,
you might not find it. For example, try this:
X = linspace(1,2)
for i=1:length(X)
Y(i) = sin(X(i))
end
plot(X, Y)
You can see in the plot that the value of sin (x) goes through 0.9 in this range, but
if you search for the index where Y(i) == 0.9, you will come up empty.
106 Chapter 4 Logic and vectors
for i=1:length(Y)
if Y(i) == 0.9
ans = i
break
end
end
The condition is never true, so the body of the if statement is never executed.
Even though the plot shows a continuous line, don’t forget that X and Y are
sequences of discrete (and usually approximate) values. As a rule, you should
(almost) never use the == operator to compare floating-point values. There are a
number of ways to get around this limitation; we will get to them later.
Example 4.4
Write a loop that finds the index of the first negative number in a vector and stores
t Instead of assigning –1 to it in ans. If there are no negative numbers, it should set ans to –1 (which is not a
ans if there are no negative legal index, so it is a good way to indicate the special case).
numbers, 0 would be an-
other excellent option. Not
Solution: (Link to screen cast with accompanying M-file.) Let’s start by creating
only is 0 not a valid element
an array of fake data.
index, but in the context of
logicals, 0 means false (or
>> X = 6:-1:-4;
not true).
The first negative number is –1 and it is the 8th element of the vector X.
Listing 4.9 negative_element.m
1 % Find the index of the first negative number in vector X
2 %
3 % Precondition: vector X
4 % Postcondition: ans will contain the index of the first negative number
5 % of vector X
6
7 % Initialize ans with value of -1, so if there is no negative number
8 % it will return this value.
9 ans = -1;
10
11 for i=1:length(X)
12 if X(i) < 0
13 ans = i
14 break
15 end
16 end
>> negative_element
ans = 8
4.18 Spoiling the fun 107
Y = X .^ 2
X = linspace(0, 2*pi)
Y = sin(X)
plot(X, Y)
Finally, the find function can perform search operations, but understanding it
requires a couple of concepts we haven’t got to, so for now you are better off on
your own. We will take a look at it later.
I started with simple loops because I wanted to demonstrate the basic con-
cepts and give you a chance to practice. At some point you will probably have
to write a loop for which there is no MATLAB shortcut, and you have to work
your way up from somewhere. If you understand loops and you are comfortable
with the shortcuts, feel free to use them! Otherwise, you can always write out the
loop. Note, if you are interested in high performance computing applications,
vector operations with MATLAB are significantly more efficient than the use of
for loops.
Example 4.5
Write an expression that computes the sum of the squares of the elements of a
vector.
Solution: Assume we have a vector X. Based on what you just read you might try:
>> X = 1:10;
>> X2 = X.^2;
>> sum_square = sum(X2)
sum_square = 385
But if we search for “sum squared,” you will find that MATLAB can do even better.
4.19 Strings
The chapter is titled “Logic and vectors”. Now that we have discussed them both,
I would like to take a minute to discuss strings. In you code you may find it
desirable to compare strings in a logical statement. You can do this, but you need
to be very careful. When you create a string, MATLAB saves the string as a vector,
with the index going 1 to the number of letters, from left to right.
>> test
test = Hello
>> test(1)
ans = H
>> test(4)
ans = l
Later we will discuss the use of applying logical operators on vectors. But
two cautionary notes. When comparing strings: 1) MATLAB will compare each
element for equivalence and return a vector of 1’s and 0’s, 2) MATLAB is case
sensitive. You will notice that I tend to avoid logical comparisons with strings, but
there are always students in class that immediately want to use strings in logical
comparisons, so I mention this here.
4.20 Examples
Example 4.6
The ratio of consecutive Fibonacci numbers, F n+1 /F n , converges to a constant
value as n increases. Write a script that computes a vector with the first n elements
of a Fibonacci sequence (assuming that the variable n is defined), and then com-
putes a new vector that contains the ratios of consecutive Fibonacci numbers. Plot
this vector to see if it seems to converge. What value does it converge on?
(Running, the vector seems to converge on a value of 1.6180.)
4.20 Examples 109
Example 4.7
A certain famous system of differential equations can be approximated by a system
of difference equations that looks like this:
xi + σ y i − xi d t
¡ ¢
x i +1 = (4.1)
£ ¤
y i +1 = y i + x i (r − z i ) − y i d t (4.2)
¡ ¢
z i +1 = z i + x i y i − bz i d t (4.3)
• Write a script that computes the first 10 elements of the sequences X , Y and
Z and stores them in vectors named X, Y and Z.
Use the initial values X 1 = 1, Y1 = 2 and Z1 = 3, with values σ = 10, b = 8/3
and r = 28, and with time step d t = 0.01.
• Read the documentation for plot3 and comet3 and plot the results in 3
dimensions.
• Once the code is working, use semi-colons to suppress the output and then
run the program with sequence length 100, 1000 and 10000.
• Run the program again with different starting conditions. What effect does it
have on the result?
• Run the program with different values for σ, b and r and see if you can get a
sense of how each variable affects the system.
4.20 Examples 111
Solution:
Listing 4.11 diffe_sequence.m
1 % Solving the certain famous system of differential equations using the
2 % equations and conditions provided in the problem.
3 %
4 % Pre-condition: None. I will use provided parameters/settings
5 % Post-condition: 3-dimensional plots of the results as a function of
6 % time-step. The numerical results will be stored to the
7 % vectors X, Y, and Z.
8
9 % Clearing the variables just in case, since X, Y, and Z, are popular
10 % choices, or in case you just ran the script with a different number of
11 % time-steps.
12 clear X Y Z;
13
14 % The number of timesteps
15 n = 10000;
16
17 % Since we know the final size of vectors X, Y and Z, let's pre-size them
18 X = zeros(1,n);
19 Y = zeros(1,n);
20 Z = zeros(1,n);
21
22 % Initial values
23 X(1) = 1;
24 Y(1) = 2;
25 Z(1) = 3;
26 % Parameters
27 sigma = 10;
28 b = 8/3;
29 r = 28;
30 % Time-step
31 dt = 0.01;
32
33 for i = 1:(n-1)
34 X(i+1) = X(i)+sigma*(Y(i)-X(i))*dt;
35 Y(i+1) = Y(i)+(X(i)*(r-Z(i))-Y(i))*dt;
36 Z(i+1) = Z(i)+(X(i)*Y(i)-b*Z(i))*dt;
37 end
38 % For the first part of the problem we are asked to dispaly the values
39 % when n <= 10
40 if n <= 10
41 disp(X)
42 disp(Y)
43 disp(Z)
44 end
45 % Only generate 1 of the plots. Uncomment 1 and comment the other
46 % to switch
47 %plot3(X,Y,Z)
48 comet3(X,Y,Z)
112 Chapter 4 Logic and vectors
Example 4.8
The logistic map is often cited as an example of how complex, chaotic behavior
can arise from simple non-linear dynamical equations. It was popularized in a
seminal 1976 paper by the biologist Robert May. 1
It has been used to model the biomass of a species in the presence of limiting
factors such as food supply and disease. In this case, there are two processes at
work: (1) A reproductive process increases the biomass of the species in proportion
to the current population. (2) A starvation process causes the biomass to decrease
at a rate proportional to the carrying capacity of the environment less the current
population.
Mathematically this can be written as
X i +1 = r X i (1 − X i )
where X i is a number between zero and one that represents the biomass at year i ,
and r is a positive number that represents a combined rate for reproduction and
starvation.
• Write a script named logmap that computes the first 50 elements of X with
r=3.9 and X1=0.5, where r is the parameter of the logistic map and X1 is
the initial population.
• Plot the results for a range of values of r from 2.4 to 4.0. How does the
behavior of the system change as you vary r .
• One way to characterize the effect of r is to make a plot with r on the x-axis
and biomass on the y axis, and to show, for each value of r , the values of
biomass that occur in steady state. See if you can figure out how to generate
this plot.
1 This was adopted from the “Logistic map” Wikipedia page, which you can see for additional
details.
4.20 Examples 113
Solution:
Listing 4.12 logmap_1.m
1 % The logistic map. Note that I will save my files as
2 % logmap_*, where * will be 1, 2, or 3 for the first, second, and
3 % third bullet respectively.
4 %
5 % Pre-condition: None. I will hard-code everything
6 % Post-condition: Vector X will have the first 50 elements of the series.
7 % That is, it will contain the population for years 1 to
8 % 50, corresponding to the index number.
9
10 % Clearing the variable just in case since X is a popular choice. This is
11 % also a good idea in case you previously ran the script with a different
12 % value of n. I'm not sure if I have mentioned it before, but note that
13 % clear is another one of those commands where we do not need a ; to
14 % supress output... there isn't any to supress.
15 clear X
16
17 n = 50;
18
19 % Since we know the final size of X, let's pre-size the vector
20 X = zeros(1,n);
21
22 % Initial population
23 X(1) = 0.5;
24
25 % Combined rate for reproduction and starvation
26 r = 3.9;
27
28 for i=1:(n-1)
29 X(i+1)=r*X(i)*(1-X(i));
30 end
31
32 % Plotting the result
33 plot(X)
34 xlabel('i')
35 xlim([1,n])
36 ylabel('X')
37
38 % Saving and printing the figure
39 savefig('logmap_1.fig')
40 print('-depsc','logmap_1.eps')
114 Chapter 4 Logic and vectors
Note that when plotting the result for multiple values of r , it might be nice to add
a legend. I did not do so here as the plot was already very busy. However, doing
so is straightforward and I will demonstrate it for you as an example. First, this is
greatly facilitated by the command num2str which takes an argument of a number,
and converts it to a string. Second, we update the plot command to name each
data set as it is plotted. This is accomplished with the addition of ’DisplayName’
followed by the string you would like to use to label the data set. Third, since we
have already attached names to the data sets, we need just tell MATLAB to display
the legend with the command legend. Have a look at my updated code below.
Also, this is a very nice example of how to efficiently add legends to plots with
multiple data sets, that is particularly useful with for loops. Know that I had never
did this before this example. How do you think I figured it out? If you said I looked
in the MATLAB documentation, then you are correct. Have a look at the documen-
tation page appropriately titled: “Add Legend to Graph.” It contains a section with
an example to “Create Simple Legend,” which is what we discussed previously,
followed by a a section with an example to “Specify Labels Using DisplayName.”
While not used here, I will also point out that for a long legend like ours, you could
look at the section “Legend with Multiple Columns” to learn how to to create a
legend with multiple columns. So the moral of the story is, if there is anything you
ever wished you could do with MATLAB, search the documentation.
Listing 4.14 logmap_2b.m
1 % The logistic map. Note that I will save my files as
2 % logmap_*, where * will be 1, 2, or 3 for the first, second, and
3 % third bullet respectively.
4 %
5 % This version has been added to demonstrate the use of num2str to add a
6 % legend.
7 %
8 %
9 % Pre-condition: None. I will hard-code everything
10 % Post-condition: Vector X will have the first 50 elements of the series.
11 % That is, it will contain the population for years 1 to
12 % 50, corresponding to the index number. In this case, it
13 % will be the last value of r we consider in the vector.
14 % More imporant will be our plot of X for values of r
15
16 % Clearing the variable just in case since X is a popular choice. This is
17 % also a good idea in case you previously ran the script with a different
18 % value of n.
19 clear X
20
21 % Initial population
22 X(1) = 0.5;
23
24 % Combined rate for reproduction and starvation
25 % For part b, let's make r a vector of values from 2.4 to 4 in increments
26 % of 0.2 using the extended colon operator. Note I will use R instead of r
27 % because now it is a vector.
28 R = 2.4:0.2:4.0;
29
30 n = 50;
31
116 Chapter 4 Logic and vectors
Example 4.9
We have worked with the Antoine equation for ethanol in several problems now.
Here we will continue the fun. Let’s revisit Example 1.5, 2.4, and 3.9. When working
with the Antoine equation, it is important to take note of the specified temperature
range over which use of the expression is recommended. While sometimes you
have no choice but to extrapolate your Antoine equation beyond this range, it
certainly is not a best practice.
a) Update your script from Example 2.4 part a) to compute vapor pressure to
check that the specified temperature is within the range of applicability. If the
temperature is outside the range, how might you communicate this to the user?
b) Update your script from Example 2.4 part c) to compute saturation temperature
to check that the specified pressure is within the range of applicability. If the
pressure is outside the range, how might you communicate this to the user?
c) Modify your script from Example 3.9 so that in the loop it stores the value
of T and log10 p sat to a vector. Then once the loop is complete, create your
Clapeyron plot using your vectors. Compare to your graph from Example 3.9.
Note that when plotting vectors, MATLAB plots all of the points at once. For
this case you therefore no longer need hold on. Represent your data as a line
with a circle for each data point. Label your axis and give your plot a title.
Solution: For this exercise we are updating solutions to previous problems with
some of the new tricks we have learned in this chapter. Namely in (a) and (b) we
will add logical checks so that the calculation is only performed under certain con-
ditions; here the logical check will be to use the Antoine equation only if it is within
the range of applicability. If the use of Antoine equation is not appropriate, we will
display a message and also store NaN to the postcondition (solution) variables. We
use NaN because it will be propagated through subsequent calculations, so that
the end result is NaN. In (a) we are updating Listing 2.5 and in (b) we are updating
Listing 2.6.
In (c) we will use a vector to store the results of each loop iteration, and will then
apply plot to vectors. Remember that vector indices start at 1. So if were were to
loop over temperature in degrees C, if the loop index starts at 0 we will need to shift
up by 1 to refer to the appropriate vector index. In (c) we are updating Listing 3.12.
4.21 CPB Examples 119
While in (c) we “shift up by 1”, this may fail if the temperature range were to change
and would certainly fail if we decided to increment the temperature by a value
other than 1. Next, let’s consider two alternatives. First, the use of a counter vari-
able is a robust and general solution to overcome these limitations. Here’s what (c)
might look like using a counter variable (c):
By counter variable, I just mean using a variable to keep track of the loop iteration
number.
But we can do even better. Let’s start by creating a vector of our temperatures, then
we can just loop over the vector indices. This will also allow us to pre-size all of the
vectors used.
Example 4.10
We have now worked with the Maxwell-Boltzmann equation in every chapter now:
Example 1.6,2.5,and 3.10. In Example 3.10 you wrote a script to calculate the
average speed of water and ethanol over the range 0 to 100 ◦ C and plotted the
results. Here, repeat the calculations but now store the results to vectors. Then
create a plot of your results. Use a different symbol and/or line to represent water
and ethanol. Label your axis, give your plot a title, and add a legend. Note you will
be plotting two sets of data. If you issue two separate plot commands, you will
need to use hold on.
Solution:
Similar to (c) in Example 4.9, here we are updating Listing 3.13 to store the results
of each loop iteration to a vector, and then plot the vector results. Remember that
vector indices start at 1. So when my loop index starts at 0, I will need to shift up
by 1 to refer to the appropriate vector index.
35 end
36
37 % Plotting
38 % When plotting, we will use the temperature in C, since
39 % that is what the problem statement asked for. We can
40 % easily create the temperature vector
41 t_C = 0:100;
42 % Water then ethanol
43 plot(t_C,speed_water,'-ro')
44 plot(t_C,speed_ethanol,'-bx')
45
46 % labeling the axis
47 xlabel('T [C]')
48 ylabel('speed [m/s]')
49
50 % plot title
51 title('Average speed from Maxwell-Boltzmann')
52
53 % legend.
54 % Data is plotted water then ethanol
55 legend('water','ethanol')
56
57 % Save and print
58 savefig('Example_4_10.fig')
59 print('-depsc','Exercise_4_10.eps')
4.21 CPB Examples 127
As mentioned in (c) in Example 4.9, there are some potential shortcomings of just
shifting up by 1. A more general solution would be to use a loop counter variable.
Here is what that might look like:
49
50 % labeling the axis
51 xlabel('T [C]')
52 ylabel('speed [m/s]')
53
54 % plot title
55 title('Average speed from Maxwell-Boltzmann')
56
57 % legend.
58 % Data is plotted water then ethanol
59 legend('water','ethanol')
60
61 % Save and print
62 savefig('Example_4_10_counter.fig')
63 print('-depsc','Exercise_4_10_counter.eps')
4.21 CPB Examples 129
In the spirit of improving, let’s improve it some more! Since we create a temper-
ature vector before we plot, we might as well move it up toward the top of the
program. Then for the for loop, we can just loop over the vector indices. This will
also allow us to pre-size all of our vectors.
48
49 % Plotting
50 % When plotting, we will use the temperature in C, since
51 % that is what the problem statement asked for.
52 % Water then ethanol
53 plot(T_C,Speed_water,'-ro')
54 plot(T_C,Speed_ethanol,'-bx')
55
56 % labeling the axis
57 xlabel('T [C]')
58 ylabel('speed [m/s]')
59
60 % plot title
61 title('Average speed from Maxwell-Boltzmann')
62
63 % legend.
64 % Data is plotted water then ethanol
65 legend('water','ethanol')
66
67 % Save and print
68 savefig('Example_4_10_vector.fig')
69 print('-depsc','Exercise_4_10_vector.eps')
4.21 CPB Examples 131
52
53 % legend.
54 % Data is plotted water then ethanol
55 legend('water','ethanol')
56
57 % Save and print
58 savefig('Example_4_10_vector_2.fig')
59 print('-depsc','Exercise_4_10_vector_2.eps')
4.22 Glossary
compound: A statement, like if and for, that contains other statements in an
indented body.
relational operator: An operator that compares two values and generates a logi-
cal value as a result.
logical value: A value that represents either “true” or “false”. MATLAB uses the
values 1 and 0, respectively.
flag: A variable that contains a logical value, often used to store the status of
some condition.
index: An integer value used to indicate one of the values in a vector or matrix
(also called subscript in some MATLAB documentation).
4.23 Exercises
Exercise 4.1 In Exercise 2.1 and 3.1 we created scripts to compute the surface tension
of ethanol, propane, and ethanol. Let’s keep the fun going here. First, let’s update our
script from Exercise 3.1 in (a) and (b). Then in (c) and (d) we will update our script from
Exercise 2.1.
a) Update your scripts so that the results are stored to vectors. You should have a total
of six vectors: the temperatures and corresponding surface tensions for ethanol, the
temperatures and corresponding surface tensions for propane, and the temperature
and corresponding surface tensions for acetone. Plot the vectors. Does this make it
easier to add a legend?
b) Next, update your script to eliminate the use of for loops. Instead, use elementwise
operations.
c) We wish to create a script to compute the surface tension of ethanol, propane, or
acetone, as specified by the user. How do you accomplish this? I would create a “flag”
variable to which the user should assign an integer value, where each integer maps to
a species. For example, if the flag variable is set to 1, that corresponds to performing
the calculations for ethanol. The calculation will be performed at a single temperature
as specified by the user.
d) Last, update your script to check that the temperature is within the range of applica-
bility of the equation.
Exercise 4.2 Let’s update our script from Exercise 3.2. Recall that we computed the
enthalpy of vaporization as a function of carbon number for the homologous series
of linear alkanes. In this version of the script, store the results to vectors. Use one
vector to store the carbon number, and another to store the corresponding enthalpy of
vaporization. Plot the results as before.
Once you script is working, have a look at documentation pages for diff. Use diff
to compute the changes in enthalpy of vaporization. Are they constant?
Exercise 4.3 Update your script from Exercise 3.3. First, update your code to eliminate
the use of a for loop. To accomplish this, you should create a vector of temperatures and
then use elementwise operations. To create a vector of temperatures from Tmin to Tmax ,
you might find the function linspace useful. Store results to a vector. Then create a plot
using your vectors.
Exercise 4.4 Update your script from Exercise 3.4, our friction factor problem. We will
update it step-by-step. Each step you will be able to run and check your code to make
sure nothing has changed. At the end of the problem we still will not make a nice Moody
diagram, but we will be closer to being able to accomplish this goal.
1. First, update you script so that each loop iteration, the computed friction factor is
stored to a vector.
2. Next, rather than use Re as a loop index variable, at the beginning of your script
create a vector of Re values. Then in your loop, loop over the indices (1 to n) of this
vector and use the appropriate element each iteration.
3. Next, update your code to use elementwise operations to eliminate the need for a
for loop.
134 Chapter 4 Logic and vectors
With the use of elementwise operations, you should find that your code is much
more efficient and hopefully more compact. Before getting to the creation of a Moody
diagram, we will discuss more the creation of our vector of Re values for a desired, larger
range, and we will also need log axes.
Chapter 5
Functions
If you work through the chapter and believe these goals are not met, please
re-review the material and reach out for help.
135
136 Chapter 5 Functions
a1 = 1;
total = 0;
for i=1:10
a = a1 * 0.5^(i-1);
total = total + a;
end
ans = total
If you were using any of those variable names before calling this script, you might
be surprised to find, after running the script, that their values had changed. If you
have two scripts that use the same variable names, you might find that they work
separately and then break when you try to combine them. This kind of interaction
is called a name collision.
As the number of scripts you write increases, and they get longer and more
complex, name collisions become more of a problem. Avoiding this problem is
one of the motivations for functions.
In the example above, after storing the final result to ans we could clear all
of the variables used: clear a1, total, i, a. This is one of the main effect of
using a function.
5.2 Functions
A function is like a script, except
• Each function has its own workspace, so any variables defined inside a
function only exist while the function is running, and don’t interfere with
variables in other workspaces, even if they have the same name. (See the
MATLAB documentation page “Base and Function Workspaces”).
t Just as with for loops and To define a new function, you create an M-file with the name you want, and put a
if statements, there is no function definition in it. For example, to create a function named myfunc, create
need for a semicolon after
an M-file named myfunc.m and put the following definition into it.
the signature line.
Listing 5.1 myfunc.m
1 function res = myfunc(x)
2 s = sin(x)
3 c = cos(x)
4 res = abs(s) + abs(c)
5 end
The first word of the file has to be the word function, because that’s how MAT-
LAB tells the difference between a script and a function file. (See the MATLAB
documentation page “Create Functions in Files”.)
5.2 Functions 137
You can create a new M-file in MATLAB’s editor using the New Script button,
just as we have used previously to create a script; this is what I will use throughout
the text. Alternatively, you can use the New button, then select Function from
the dropdown menu. This will create a new file as before, but will also include a
template for creating a function.
t When you use the New
button to create a new func-
tion, you will see it puts the
output variable in brackets.
Brackets are required if you
want more than one out-
put variable. But more on
having multiple/optional
output variables later as
there is more than meets
eye.
>> y=myfunc(1);
s = 0.84147
c = 0.54030
res = 1.3818
t Notice that I changed both While you are debugging a new function, you might want to display interme-
the name of the M-file and diate results like this, but once it is working, you will want to add semi-colons
the name of the function. to make it a silent function. Most built-in functions are silent; they compute a
We will discuss my reason result, but they don’t display anything (except sometimes warning messages).
for this momentarily.
>> y=myfunc_silent(1)
y = 1.3818
Each function has its own workspace, which is created when the function
starts and destroyed when the function ends. If you try to access (read or write)
the variables defined inside a function, you will find that they don’t exist.
>> s
Undefined function or variable 's'.
The only value from the function that you can access is the result, which in this
case is assigned to y.
If you have variables named s or c in your workspace before you call myfunc,
they will still be there when the function completes.
5.3 Documentation 139
>> s = 1;
>> c = 1;
>> y = myfunc(1);
s = 0.84147
c = 0.54030
res = 1.3818
>> s, c
s = 1
c = 1
So inside a function you can use whatever variable names you want without
worrying about collisions.
5.3 Documentation
At the beginning of every function file, you should include a comment that ex-
plains what the function does.
Listing 5.3 myfunc_comment.m t When you use the New
button to create a new func-
1 % res = myfunc_comment(x) tion, you will see it puts the
2 % compute the Manhattan distance from the origin to the
documentation material
3 % point on the unit circle with angle (x) in radians.
after the function signature.
4
5 function res = myfunc_comment(x) Both formats are correct
6 s = sin(x); and have the same effect
7 c = cos(x); (give it a try). I prefer to put
8 res = abs(s) + abs(c); the comments/documen-
9 end tation at the top of a file,
the same convention we
used when writing scripts.
When you ask for help, MATLAB prints the comment you provide. This is also why I use the
New Script button to create
>> help myfunc_comment function files. Personally,
res = myfunc_comment(x) the least I need to try and
Compute the Manhattan distance from the origin to the remember the better.
point on the unit circle with angle (x) in radians.
There are lots of conventions about what should be included in these com-
ments. Among other things, it is a good idea to include:
• The signature of the function, which includes the name of the function, the
input variable(s) and the output variable(s).
and includes only information that someone using the function needs to
know. You can put additional comments inside the function that explain
the details.
• An explanation of what the input variables mean; for example, in this case
it is important to note that x is considered to be an angle in radians.
For the first two gotcha’s, I would again point you to the MATLAB documentation
page “Create Functions in Files.”
The third gotcha is that your function names can collide with built-in MATLAB
functions. For example, if you create an M-file named sum.m and save it in your
current folder, then MATLAB will always call this new function and not the built-in
version. This is a bad idea, especially if you plan to share your code with others.
The unsuspecting user may use the command sum and expect to get MATLAB’s
built-in function, but instead get the function you created. As an example, put
the following code in a file named sum.m:
Listing 5.5 sum.m
1 function res = sum(x)
2 res = 7;
3 end
>> sum(1:3)
ans = 7
While this is correct using your code, it is confusing to the unknowing user. Before
you create a new function, check to see if there is already a MATLAB function with
the same name. If there is, choose another name!
MATLAB will attempt to help you if you attempt the third gotcha. When I cre-
ated the function sum, the editor happily allowed me to save the file. However, the
following message was displayed in the Command Window : Warning: Function
sum has the same name as a MATLAB built-in. We suggest you rename
the function to avoid a potential name conflict.
Note that beside name collisions with other functions, name collisions with t Before we move on to Ex-
variables exist too! So take a minute when you name your function. For more amples, recall from our
discussion in Chapter 4 that
insight on the third gotcha, have a look at the MATLAB documentation pages
MATLAB’s built-in func-
“Files and Folders that MATLAB Accesses and “Function Precedence Order.” tions operate element-wise
on vectors. The same is
Example 5.1 true with functions that
you write, so long as you
MATLAB contains a function sqrt to compute the square root of a number. Write use element-wise opera-
a function named cubert to compute the cube root of a number. tions (i.e., .*). Remember,
a scalar is a 1 by 1 matrix,
Solution: (Link to screen cast and accompanying M-file.) The cube root of x is so element-wise operators
x 1/3 . Let’s write the function. work as normal with scalars.
Listing 5.6 cubert.m So writing your functions
in this way is not a problem.
1 % res = cubert (n) However, in this Chapter we
2 % Computing the cube root of a number n. will only consider functions
3 function res = cubert (n)
that pass and return scalars.
4 res = n^(1/3);
Functions of vectors will be
5 end
discussed later.
142 Chapter 5 Functions
Beginning with a new MATLAB session, let’s first take a look at our workspace.
>> who
>>
Nothing. We currently do not have any defined variables in our workspace. Now
let’s use our function cubert to compute the cube root of 27, which we know
should be 3.
>> cubert(27)
ans = 3
>> who
Your variables are:
ans
The only defined variable in our workspace is ans, the default variable that the
result of a calculation is stored to. Notice that while res and n are used in our func-
tion, neither appear in our workspace. While ans is a great variable, the problem is
it will be over-written the next time we perform a calculation. It is a better practice
to store the result of the calculation to a unique variable.
>> x = cubert(27)
x = 3
Lastly, remember that a variable is equal to the number that is assigned to it. So
you can also pass variables to functions. This is especially good if it makes your
code more readable. And the variable need not be n, what we call the passed vari-
able within the function; MATLAB doesn’t “see” the variable name, just the number.
>> k = 27;
>> x = cubert(k)
x = 3
In addition to calling a variable from the Command Window , you can call it from
a script. (You may find this useful when submitting homework solutions.) After
all, a script is just a series of commands you could enter one-by-one from the
Command Window . Additionally, you could call a function from within a function.
And beginning with MATLAB 2016b, you can now add functions to scripts (i.e., put
a “local” function directly in a script file). We will discuss multiple functions in a
file in the next chapter.
5.5 Multiple input variables 143
Example 5.2
MATLAB contains a function sin which computes the sine of an angle x, where x
is in units of radians, and a function sind where the angle is in units of degrees.
Write a new function sin_deg which computes the sine of an angle x in degrees.
(I.e., Your own version of sind.) Your function should convert the angle from
radians to degrees, and then use sin to compute the sine of the angle.
Note: In practice I would always recommend using MATLAB’s built-in functions,
so long as you know they exist and how they work. This way you can be guar-
anteed they are bug free and optimized for efficiency. Here, this is an academic
exercise, and the availability of sind provides a means for you to directly check
the correctness of your code.
>> y = sin_deg(90)
y = 1
>> y = sin_deg(180)
y = 1.2246e-16
This function computes the length of the hypotenuse of a right triangle if the
lengths of the adjacent sides are a and b using the Pythagorean Theorem. (Note:
there is a MATLAB function called hypot that does the same thing.)
If we call it from the Command Window with arguments 3 and 4, we can
confirm that the length of the third side is 5.
>> c = hypotenuse(3, 4)
c = 5
The arguments you provide are assigned to the input variables in order, so in this
case 3 is assigned to a and 4 is assigned to b. MATLAB checks that you provide
the right number of arguments; if you provide too few, you get
>> c = hypotenuse(3)
Error using hypotenuse (line 2)
Not enough input arguments.
This error message might seem confusing if you were just to read the first line,
because it suggests that the problem is in hypotenuse rather than in the function
call. But the second line should clear-up what the problem is. Keep that in mind
when you are debugging.
If you provide too many arguments, you get
>> c = hypotenuse(3, 4, 5)
Error using hypotenuse
Too many input arguments.
Example 5.3
Write a function rect_area to compute the area of a rectangle of length l and
height h.
Solution: (Link to screen cast and accompanying M-file.) For a rectangle, the area
A is computed as A = l h. Let’s write a function to do this for us.
5.5 Multiple input variables 145
>> a = rect_area(2,4)
a = 8
Note that while I sometimes deviate from my own suggestion, a good habit is to
use lowercase letter variables for scalar quantities, and capital letter variables for
vectors/matrices. Remember MATLAB is case-sensitive, so this will keep you from
mixing things up. So while A for area is attractive, I instead use a since it is a scalar
quantity.
Example 5.4
Rather than having a separate function to compute the sine of an angle in radi-
ans and in degrees, create a new function sin_angle that can compute either,
depending on the specification of the user.
Notice two things. First, I use the function disp to display my error message,
which is a string. Since the output is not suppressed, it will be displayed in the
Command Window if I do not provide a valid value for units. Second, if I do not
provide a valid value of units, NaN is assigned to res. Remember NaN stands for
“Not a Number”, and will be propagated though subsequent calculations. I will run
sin_angle passing a value of units = 2 to confirm this result, and will multiply
ans by 2 and then 0 to confirm that the result is still be NaN.
>> sin_angle(180,2)
Incorrect choice of units. Please try again.
ans = NaN
>> ans*2
ans = NaN
>> ans*0
ans = NaN
Nice!
>> isprime(17)
ans = 1
>> isprime(21)
ans = 0
The functions isscalar and isvector check whether a value is a scalar or vector;
if both are false, you can assume it is a matrix (at least for now).
To check whether a value you have computed is an integer, you might be
tempted to use isinteger. But that would be wrong, so very wrong. isinteger
checks whether a value belongs to one of the integer types (a topic we have not
discussed); it doesn’t check whether a floating-point value happens to be an inte-
ger.
>> c = hypotenuse(3, 4)
c = 5
>> isinteger(c)
ans = 0
To do that, we have to write our own logical function, which we’ll call isintegral:
Listing 5.12 isintegral.m
1 % res = isintegral(x)
2 % checks whether or not x is an integer. If it is, it returns
3 % a value of 1 for true. If not, it returns a value of 0 for false.
4 function res = isintegral(x)
5 if round(x) == x
6 res = 1;
7 else
8 res = 0;
9 end
10 end
This function is good enough for most applications, but remember that floating-
point values are only approximately right; in some cases the approximation is an
integer but the actual value is not.
Example 5.5
Write a function iseven to check if an integer is even.
Hint: An even number is a number that when divided by 2 has a remainder of 0.
This is called the modulus after division, and MATLAB has a function to compute it,
mod. Type help mod and have a look at the documentation. If we were interested
instead in odd numbers, an odd number is a number that when divided by 2 has a
remainder of 1.
148 Chapter 5 Functions
>> iseven(222)
ans = 1
>> iseven(211)
ans = 0
What if you had never heard of the term “modulus” before? Well, using the hint
that an even number is a number that when divided by 2 has a remainder of 0, we
can cleverly use the round command.
Additionally note that beside mod, MATLAB has a built-in function rem to compute
the remainder after division. Using either in this example would yield the same
5.7 An incremental development example 149
result. The difference between mod and rem is provided in the documentation for
rem if you are interested, under the header “Difference between rem and mod.”
• Write a script named find_triples and start with a simple statement like
x=5.
• Use an if statement to print only the triples a, b and c that pass the test.
• Generalize the function to take input variables that specify the range to
search.
So the first draft of this program is x=5, which might seem silly, but if you start
simple and add a little bit at a time, you will avoid a lot of debugging.
Here’s the second draft:
for a=1:3
a
end
At each step, the program is testable: it produces output (or another visible effect)
that you can check.
for a=1:3
a
for b=1:4
b
end
end
The inner loop gets executed 3 times, once for each value of a, so here’s what the
output loops like (I adjusted the spacing to make the structure clear):
>> find_triples
a = 1 b = 1
b = 2
b = 3
b = 4
a = 2 b = 1
b = 2
b = 3
b = 4
a = 3 b = 1
b = 2
b = 3
b = 4
for a=1:3
for b=1:4
c = hypotenuse(a, b);
[a, b, c]
end
end
>> find_triples
Sharp-eyed readers will notice that we are wasting some effort here. After
checking a = 1 and b = 2, there is no point in checking a = 2 and b = 1. We can
eliminate the extra work by adjusting the range of the second loop:
for a=1:3
for b=a:4
c = hypotenuse(a, b);
[a, b, c]
end
end
If you are following along, run this version to make sure it has the expected effect.
>> find_triples
ans = 1.0000 1.0000 1.4142
ans = 1.0000 2.0000 2.2361
ans = 1.0000 3.0000 3.1623
ans = 1.0000 4.0000 4.1231
ans = 2.0000 2.0000 2.8284
ans = 2.0000 3.0000 3.6056
ans = 2.0000 4.0000 4.4721
ans = 3.0000 3.0000 4.2426
ans = 3 4 5
for a=1:3
for b=a:4
c = hypotenuse(a, b);
flag = isintegral(c);
[c, flag]
end
end
By not displaying a and b I made it easy to scan the output to make sure that
the values of c and flag look right.
>> find_triples
ans = 1.4142 0
ans = 2.2361 0
ans = 3.1623 0
ans = 4.1231 0
ans = 2.8284 0
ans = 3.6056 0
ans = 4.4721 0
ans = 4.2426 0
ans = 5 1
I chose the ranges for a and b to be small (so the amount of output is man-
ageable), but to contain at least one Pythagorean triple. A constant challenge of
debugging is to generate enough output to demonstrate that the code is working
(or not) without being overwhelmed.
The next step is to use flag to display only the successful triples:
for a=1:3
for b=a:4
c = hypotenuse(a, b);
flag = isintegral(c);
if flag
[a, b, c]
end
end
end
>> find_triples
ans = 3 4 5
5.10 Encapsulation and generalization 153
The empty parentheses in the signature are not strictly necessary, but they make it
apparent that there are no input variables. Similarly, when I call the new function,
I like to use parentheses to remind me that it is a function, not a script; but again,
here too the use of the parentheses is optional since there are no input variables.
>> find_triples()
ans = 3 4 5
The output variable (res) isn’t strictly necessary, either; it never gets assigned a
value. But I put it there as a matter of habit, and also so my function signatures all
have the same structure. For now, we are just displaying the triples. Later in the
text, you will learn how you can pack all of the triples up into a matrix and return
it. But we will save that for later.
The next step is to generalize this function by adding input variables. The
natural generalization is to replace the constant values 3 and 4 with a variable so
we can search an arbitrarily large range of values.
154 Chapter 5 Functions
>> find_triples(15)
ans = 3 4 5
ans = 5 12 13
ans = 6 8 10
ans = 8 15 17
ans = 9 12 15
Some of these are more interesting than others. The triples 5, 12, 13 and 8, 15, 17
are “new,” but the others are just multiples of the 3, 4, 5 triangle we already knew.
5.11 A misstep
When you change the signature of a function, you have to change all the places
that call the function, too. For example, suppose I decided to add a third input
variable to hypotenuse:
When d is 2, this does the same thing it did before. There is no practical reason
to generalize the function in this way; it’s just an example. Now when you run
find_triples, you get:
5.12 continue 155
>> find_triples(20)
Error using hypotenuse (line 2)
Not enough input arguments.
So that makes it pretty easy to find the error. This is an example of a development
technique that is sometimes useful: rather than search the program for all the
places that use hypotenuse, you can run the program and use the error messages
to guide you.
But this technique is risky, especially if the error messages make suggestions
about what to change. If you do what you’re told, you might make the error
message go away, but that doesn’t mean the program will do the right thing.
MATLAB doesn’t know what the program is supposed to do, but you should.
And that brings us to the Eighth Theorem of debugging:
Error messages sometimes tell you what’s wrong, but they seldom
tell you what to do (and when they try, they’re usually wrong).
5.12 continue
As one final improvement, let’s modify the function so that it only displays the
“lowest” of each Pythagorean triple, and not the multiples. The simplest way to
eliminate the multiples is to check whether a and b share a common factor. If
they do, then dividing both by the common factor yields a smaller, similar triangle
that has already been checked.
MATLAB provides a gcd function that computes the greatest common divisor
of two numbers. If the result is greater than 1, then a and b share a common
factor and we can use the continue statement to skip to the next pair. This brings
us to the final version of our function:
156 Chapter 5 Functions
continue causes the program to end the current iteration immediately (without
executing the rest of the body), jump to the top of the loop, and “continue” with
the next iteration.
In this case, since there are two loops, it might not be obvious which loop to
jump to, but the rule is to jump to the inner-most loop (which is what we wanted).
I also simplified the program slightly by eliminating flag and using isintegral
as the condition of the if statement.
Here are the results with n=40:
>> find_triples(40)
ans = 3 4 5
ans = 5 12 13
ans = 7 24 25
ans = 8 15 17
ans = 9 40 41
ans = 12 35 37
ans = 20 21 29
2 2
(F n F n+3 , 2F n+1 F n+2 , F n+1 + F n+2 )
is a Pythagorean triple for all n ≥ 1.
5.12 continue 157
Example 5.6
Write a function named fib_triple that takes an input variable n, uses new
function fibonacci_seq to compute the first n Fibonacci numbers, and then
checks whether this formula produces a Pythagorean triple for each number in
the sequence.
Note that fibonacci_seq will just be Listing 4.3 on page 93 converted from a
script to a function.
Solution:
Listing 5.16 fib_triple.m
1 % res = fib_triple(n)
2 % checks whether the provided formula using 3 consecutive Fibonacci
3 % numbers produces a Pythagorean triple over the range 1 to n.
4 % The result will be a vector containing 1 (for true) and 0 (for false)
5 % for an n corresponding to the index.
6 function res = fib_triple(n)
7 F = fibonacci_seq(n+3);
8 for i = 1:n
9 a = F(i) * F(i+3);
10 b = 2 * F(i+1) * F(i+2);
11 c = F(i+1)^2 + F(i+2)^2;
12 % Will be 1 if true (it is a triple), 0 if not
13 res(i) = istriple(a, b, c);
14 end
15 end
Note that while I could combine my code into a single function, I have chosen
to modulate my code and create three separate function files. This helps make
my code more readable, and additionally helps reduce the occurrence of bugs.
Independent of fib_triple, I can write istriple and fibonacci_seq, and
test/debug them independently. Not to mention, this would facilitate the use of
istriple and fibonacci_seq by other functions too. Also note here that I have
functions with vector inputs and outputs. We will discuss this further later.
1. Before the function starts running, MATLAB creates a new workspace for it.
2. MATLAB evaluates each of the arguments and assigns the resulting values,
in order, to the input variables (which live in the new workspace).
3. The body of the code executes. Somewhere in the body (often the last line)
a value gets assigned to the output variable.
4. The function’s workspace is destroyed; the only thing that remains is the
value of the output variable and any side effects the function had (like
displaying values or creating a figure).
5. The program resumes from where it left off. The value of the function call
is the value of the output variable.
When you are reading a program and you come to a function call, there are
two ways to interpret it:
• You can think about the mechanism I just described, and follow the execu-
tion of the program into the function and back, or
5.14 Examples 159
• You can take the “leap of faith”: assume that the function works correctly,
and go on to the next statement after the function call.
When you use built-in functions, it is natural to take the leap of faith, in part
because you expect that the built-in MATLAB functions work, and in part because
you don’t generally have access to the code in the body of the function.
But when you start writing your own functions, you will probably find yourself
following the “flow of execution.” This can be useful while you are learning, but
as you gain experience, you should get more comfortable with the idea of writing
a function, testing it to make sure it works, and then forgetting about the details
of how it works.
Forgetting about details is called abstraction; in the context of functions,
abstraction means forgetting about how a function works, and just assuming
(after appropriate testing) that it works.
5.14 Examples
Example 5.7
Write a function isodd to check if an integer is odd. Once you are certain isodd
is working correctly, write a function that multiplies two numbers (say m and n) if
and only if only one of the numbers is odd. Otherwise, add them together.
Note, this may seem like a silly thing to do. But what I want is for you to try and use
a logical function as the condition in an if statement. Also, before writing isodd,
have a look at iseven in Example 5.5.
Solution:
Listing 5.19 isodd.m
1 % res = isodd(n)
2 % A function to determine if integer n is odd. An odd number is a
3 % number that when divided by 2 the remainder is 1. We will compute the
4 % remainder of dividing n by 2 using the MATLAB function mod. If
5 % mod(n,2) is found to be 1, we have an odd number so set res = 1 (true).
6 % Otherwise, set res = 0 (false).
7 %
8 function res = isodd(n)
9 r = mod(n,2);
10 if r == 1
11 res = 1;
12 else
13 res = 0;
14 end
15 end
160 Chapter 5 Functions
Example 5.8
Let’s keep working with Antoine’s equation (the subject of Examples 1.5, 2.4, 3.9,
and 4.9.
a) Write a function to compute p sat using the Antoine equation. The user should
provide as input variables the parameters A, B , and C , along with the desired
temperature. In your documentation, please be sure to inform the user of the
units.
b) Building on (a), could you allow the user to choose what units they would like
the pressure returned in?
c) Write a function to generate a Clapeyron plot using Antoine’s equation (i.e.,
plot log10 p sat versus 1/T ). The user should provide as input variables the
parameters A, B , and C , along with the desired temperature range (Tmin and
Tmax ). In your documentation, please be sure to inform the user of the units.
Note than when you generate a plot from within a function, it will be displayed
in a new Figure Window .
5.15 CPB Examples 161
Solution:
a) The first part to this problem is straightforward. We will create a function that
reads in the Antoine parameters A, B, and C (which assume the temperature
is in ◦ C and the pressure is in mmHg), and the temperature of interest in ◦ C,
and returns the vapor pressure in mmHg. Notice that I do use element-wise
operations, so our function is vectorized and you could pass a vector of tem-
peratures for which to perform calculations.
b) Next, we update our function to take on the an additional flag variable that
indicates which units to use. In my case, units will be an integer between 1
and 4. I like to use integer values for flag variables as they facilitate logical
comparisons. Rather than re-type my Antoine code, I call the function we just
wrote. While this may seem silly, it is a case of incremental development. Since
our previous code was bug free, any errors introduced must solely be do to our
logical comparisons.
162 Chapter 5 Functions
You may wish instead to perform logical comparisons with strings. This is
possible, but you need to be careful. As we discussed previously in the text,
when you create a string, MATLAB saves the string as a vector, with the index
going 1 to the number of letters, from left to right. When comparing strings: 1)
MATLAB will compare each element for equivalence and return a logical vector
of 1’s and 0’s, 2) MATLAB is case sensitive.
c) Last but not least, we will create a function to create a Clapeyron plot. I will not
use a for loop but instead will use vector operations. In this case I directly per-
form the Antoine calculation, although we could just as well used our function
from the first part of this problem.
5.15 CPB Examples 163
Example 5.9
We wrote a function to perform Antoine equation calculations, how about the
Maxwell-Boltzmann equation? (Examples 1.6, 2.5, 3.10, 4.10). Write a function
mb_speed that takes as input molecular weight and temperature, and returns the
average molecular speed. Please be sure to communicate to the user the units of
molecular weight, temperature, and the speed.
164 Chapter 5 Functions
Solution: As was the case for our Antoine function, this problem is straightforward.
We will encapsulate and convert our Maxwell-Boltzmann script to a function.
Example 5.10
A central goal of phase-equilibrium thermodynamics is understanding, modeling,
and predicting the pvT behavior of a fluid. By pvT , we mean the relationship
between pressure (p), molar volume (v), and the temperature (T ). For a single
component, single phase system, two intensive thermodynamic properties are
needed to fix the state of the system. So if we specify T and p, we can compute
v. Put differently, v = f (T, p). Note that p, v, and T , are all intensive, so any two
could be used to fix the state of the system (and to compute the third property).
For this exercise we will write two functions to compute v at a specified T and p
using two different methods. For all cases, we will apply the methods to estimate
the molar volume of n-butane at 350 K and 2 bar for which n-butane is a super-
heated vapor, and at 440 K and 60 bar for which n-butane is a superheated vapor.
(Remember 1 bar = 1 × 105 Pa.) For all cases, please report v in units of cm3 /mol.
At these conditions NIST WebBook reports v = 14, 043 cm3 /mol at 350 K and 2 bar,
and v = 172.98 cm3 /mol at 440 K and 60 bar. 1 . How do your predictions compare?
Note that you are making predictions, which will disagree with the reference data.
a) Write a function to calculate v at a specified T and p using the ideal gas equa-
tion of state. Remember pV ig = nRT so that
V ig RT
v ig = = (5.1)
n p
where the superscript “ig” is used to designate the property of an ideal gas.
b) The equation of state for a “real” fluid is pv = Z RT , where Z is the compress-
ibility factor. For a real fluid then we have
RT
v=Z = Z v ig (5.2)
p
Bp
Z ≈ 1+ (5.3)
RT
where B is the second virial coefficient. Okay, so how does one compute the
second virial coefficient? One simple method is to use a correlation based on
Pitzer’s corresponding states theory
B pc
= B (0) + ωB (1) (5.4)
RTc
Therefore
RTc £ (0)
B + ωB (1)
¤
B= (5.5)
pc
where
0.422
B (0) = 0.083 − (5.6)
Tr1.6
1 http://webbook.nist.gov/chemistry/fluid/
166 Chapter 5 Functions
0.172
B (1) = 0.139 − (5.7)
Tr4.2
where Tc and p c are the critical temperature and pressure, Tr is the reduced
temperature defined as Tr = T /Tc , and ω is the accentric factor. From NIST
WebBook for n-butane we have: Tc = 425.125 K, p c = 37.960 bar, and ω = 0.201.
Write a function to calculate v at a specified T and p using the truncated virial
equation. The user should also provide as input: Tc , p c , and ω. Your function
should use the function written for (a) to compute v ig .
Solution: Let’s start with the ideal gas equation of state. Once we have this code
working, we will be able to re-use it in our virial calculation code. The function will
have input variables of the temperature and pressure. Here I will use units of bar
for pressure and K for temperature. Within the code I will convert the pressure to
SI units, perform the calculation, and then convert the volume to the desired units
of cm3 /mol.
Let’s use the function to compute the ideal gas molar volume at T = 350 K and
p = 2 bar.
This is only slightly larger than the reference value of 14,043 cm3 /mol. Next, we
will try to account for deviations from the ideal gas limit using the truncated virial
equation. Using just the second virial coefficient, this can be thought of as a
Maclaurin series expansion truncated after the second term. We will use the “real”
gas equation of state, v = Z RT /p = Z v ig . So we need just calculate Z , which is
dimensionless, then multiply by the molar volume of an ideal gas in our preferred
units. Let’s do it!
Now let’s test it for our system. For n-butane we are given Tc = 425.125 K, p c =
37.960 bar and ω = 0.201.
Nice! The truncated virial equation predicts a molar volume of 14,044 cm3 /mol, in
excellent agreement with the reference value of 14,043 cm3 /mol.
Next, let’s look at the more challenging problem of T = 440 K and p = 60 bar where
n-butane is a supercritical fluid, and v = 172.98 cm3 /mol. Let’s look at predictions
made using the ideal gas equation of state and the truncated virial equation at
these conditions. First, the case of an ideal gas:
This is much larger than the reference value. Next, we will try to account for devia-
tions from the ideal gas limit using the truncated virial coefficient.
Using the truncated virial equation, our prediction is still off from the reference
value of v = 172.98 cm3 /mol. However, the prediction is greatly improved relative
to the ideal gas equation of state.
5.16 Glossary 169
5.16 Glossary
side-effect: An effect, like modifying the workspace, that is not the primary pur-
pose of a script.
name collision: The scenario where two scripts that use the same variable name
interfere with each other.
input variable: A variable in a function that gets its value, when the function is
called, from one of the arguments.
output variable: A variable in a function that is used to return a value from the
function to the caller.
signature: The first line of a function definition, which specifies the names of
the function, the input variables and the output variables.
logical function: A function that returns a logical value (1 for “true” or 0 for
“false”).
abstraction: The process of ignoring the details of how a function works in order
to focus on a simpler model of what the function does.
170 Chapter 5 Functions
5.17 Exercises
Exercise 5.1 “Yaws’ Critical Property Data for Chemical Engineers and Chemists”2 rec-
ommends use of a modified Watson equation to calculate the enthalpy of vaporization as
a function of temperature:
T n
µ ¶
vap
∆H = A 1− (5.8)
B
where A, B and n are regressed coefficients, T is the temperature in K, and ∆H vap is the
enthalpy of vaporization in units of kJ/mol.
Write a function to compute ∆H vap and compare against the values computed with
your script from Exercise 2.2. Your function should take as input A, B , n and T , and
return ∆H vap .
Exercise 5.2 Let’s keep building upon your friction factor code!
In your transport phenomenon course (fluid mechanics), you likely solved many
problems that required you to read values of friction constants from a Moody chart, or to
use analytic expressions for the friction factor of limited range. Recently, Díaz-Damacillo
and Plascencia published an article in AIChE Journal titled: “A New Six Parameter Model
to Estimate the Friction Factor.”3 In that work, the authors propose a new analytic
expression containing six parameters that is capable of estimating the friction factor
for flow in pipes at all conditions (i.e., Reynold’s numbers and relative roughness). The
proposed expression takes the form:
64 λ1 λ2
f = + ´+ (5.9)
τ
1 + exp τ2 −Re ·
³ ³ ´
Re 1 + exp 1 −Re ²
100 600 D
ρV D
Re =
µ
where ρ is the density of the fluid, V is the fluid flow velocity, and D is the diameter of the
pipe. The term ² is the pipe roughness, and the term ²/D is dimensionless and commonly
referred to as the relative roughness. In addition to Re and ²/D (two parameters), the other
six parameters are λ1 , λ2 , τ1 , and τ2 . The parameter λ1 is the residual stress contribution
2 Yaws, Carl L. (2012; 2013; 2014). Yaws’ Critical Property Data for Chemical Engineers
from the laminar to turbulant transition to the friction factor, λ2 is the residual stress
contribution from the pipe roughness to the friction factor, τ1 is the value of Re at which
occurs the first transition in the friction factor, and τ2 is the value of Re at which the
second transition occurs. The values of λ1 and τ1 are constant and equal to:
λ1 = 0.02
τ1 = 3000
and λ2 and τ2 are given by the following expressions:
¯ Ã !2 ¯
¯ 1 ¯
λ2 = ¯λ1 −
¯ ¯
1 ²
¡ ¢ ¯
¯ −2 log10 3.7065 · D
¯
0.77505 10.984
τ2 = ¡ ¢2 − ¡ ² ¢ + 7953.8
²
D D
As you try to keep track of units, remember that Re and ²/D are dimensionless.
a) Write a function that takes as input Re and ²/D, and returns f . As a reference to
check your code, solving I find that for Re = 1 × 105 with ²/D = 1/30 I get f = 0.0601.
With ²/D = 1/1014 I get f = 0.0207. Note that given the values of ²/D, you might also
consider instead using D/² as your input variable.
b) Next, use your function to plot f versus Re for a given value of ²/D. That is, make your
own Moody chart. Cool! For the purpose of this question, write a script or function
where you loop over Re values over the range of 1 × 103 to 1 × 105 , for each Re use your
function to compute f for the case of ²/D = 0.1 and ²/D = 0.001, and plot.
In a Moody chart, a log-scale is used for both the x- and y-axis. To do this, replace
the plot command with the loglog command. The command loglog works exactly
the same as plot, only it uses log axes. Be sure to label your axes, label your data
sets, and print your figure to file. And if you would like to include a grid in your plot,
like an actual Moody chart, use the command grid on. After you plot, execute the
command grid on.
As a future refrence, if you wanted to make a semi-log plot where only one of the axes
uses a log-scale and the other uses a linear-scale, you can use the command semilogx
or semilogy. In the next chapter we will look at having vectors as inputs and outputs
in our functions, and will revisit this problem.
Chapter 6
Functions of vectors
If you work through the chapter and believe these goals are not met, please
re-review the material and reach out for help.
173
174 Chapter 6 Functions of vectors
54 p = p_bar*1e5;
55 % The molar gas constant in units of J/(mol K), SI units
56 r = 8.314;
57 % With all SI units, computing the molar volume in m^3/mol
58 v = r*t/p;
59 % Converting the molar volume to cm^3/mol
60 res = v*(100^(3));
61 end
First, as mentioned earlier, helper functions can not be called from the
Command Window . Here is what would happen if I tried:
>> v_ideal_gas(440,60)
Undefined function or variable 'v_ideal_gas'.
The only function that MATLAB can see from the Command Window is the top-
level function. Trying instead
>> v_virial(440,60,425.125,37.960,0.201)
ans = 313.2382
It works as expected.
In writing v_virial for Exercise 5.10, we first wrote v_ideal_gas and tested
it from the Command Window to ensure it was working. We just saw that we can not
do that here. Here I might start by writing v_ideal_gas in two steps. First, I might
create a file v_virial.m and start with a top-level function named v_virial that
takes no input variables and returns no output value. I would then write a (helper)
function v_ideal_gas. My first test would be to pass a variable from v_virial
to v_ideal_gas and back to v_virial. The purpose of this is to make sure I have
my file set-up correctly and that the functions can properly communicate with
each other. This code might look like the following
>> v_virial
test_pass = 10
test_in = 10
test_return = 10
Nice! Next, I would pass from v_virial a value of the temperature and pressure
to v_ideal_gas and compute v ig to check for correctness, just as we did before
from the Command Window .
>> v_virial
test = 609.6933
Notice that I have left my comments out here to save space. Running from the
Command Window :
>> v_virial
ans = 313.2382
Last, we would pass the parameters to the function, which would bring us
back to our final code. This code was relatively simple. If you had a larger function,
you might build it up in smaller, testable parts.
For this problem I need only two functions, but if there were more, I could
write and test them one at a time, and then combine them into a working program.
example:
There’s nothing special about this function at all. The only difference from
the scalar functions we’ve seen is that I used a capital letter to remind me that X is
a vector.
This is another example of a function that doesn’t actually have a return value;
it just displays the value of the input variable:
>> display_vector(1:3)
X = 1 2 3
Here’s a more interesting example that encapsulates the code from Section 4.15
(page 102) that adds up the elements of a vector:
I called it mysum to avoid a collision with the built-in function sum, which does
pretty much the same thing.
Here’s how you call it from the Command Window :
Ideally I would have changed the name of the output variable to Res, as a reminder
that it is supposed to get a vector value, but I didn’t.
Here’s how myapply works:
>> V = myapply(1:3)
V = 1 4 9
Example 6.1
Write a function named find_negative_element that encapsulates the code,
from Example 4.9, that finds the location of (or index of) the first negative number
in a vector.
Note that if you desired, if there is no negative element, you could have MATLAB
throw an error message with the command, error.
180 Chapter 6 Functions of vectors
We notice two differences. First, the documentation is placed under the function
signature. Recall, in scripts, we always put the documentation at the top of the
file. For functions, you can add the documentation either immediately above or
below the function signature. Either will work and provide the same output when
you use help or doc. My preference is to put the documentation at the top. This
way I am using the same convention for both scripts and functions, and the top
of the file is where I am accustomed to looking for this information.
The second difference is the output variable is placed in brackets. At first
glance, after our previous discussion, it may appear that MATLAB is packing
multiple variables up into a vector to be returned. But there is more than meets
the eye. We can in fact place multiple variables separated by a comma in between
the brackets to return multiple variables. However, whether or not more than one
variable is actually returned is dependent on how the function is called. Whether
subsequent variables (the variables after the first) are actually returned is optional,
although they do need to be assigned within the function. Let’s take a look at
an example to see what I mean. I will work with our truncated virial equation
function, Listing 6.1, which we are already familiar with. In the interest of space,
I will remove all of the documentation. I will also keep appending the name of
the top function and the file so that I can save a unique copy of each iteration.
Let’s start by taking the output variable (res) in the function signature of the top
function (v_virial) and put it in brackets.
6.4 Multiple/optional output variables 181
>> v_virial_1(440,60,425.125,37.960,0.201)
ans = 313.2382
Nice! Next, imagine you might also wish to return the computed compressibility,
z, and the ideal gas molar volume, vig. We will add both to the brackets, separat-
ing the variables by a comma.
182 Chapter 6 Functions of vectors
Let’s try running as before, and let’s also try assigning the output to a variable:
>> v_virial_2(440,60,425.125,37.960,0.201)
ans = 313.2382
>> v = v_virial_2(440,60,425.125,37.960,0.201)
v = 313.2382
6.5 Vectorizing your functions 183
Hey, what gives! The compressibility and ideal gas molar volume are not returned.
The issue is we need to tell MATLAB to return them. Here’s how. First we will
return none, then one and both of the optional variables.
>> v = v_virial_2(440,60,425.125,37.960,0.201)
v = 313.2382
Cool! We see that what is returned in not a vector, but instead each is returned as
its own variable.
You will find that I do not always utilize this feature of MATLAB when I wish
to return multiple scalar variables. Many times if I wish to return multiple scalar
variables, I will often pack them up to a single vector and return it. This way I am
certain that I am returning all of the variables regardless of how I call the function.
This is just my preference and style. You are free to disagree. Just know that there
are some cases where you are not free to choose, such as when we will create
function files to be used by fsolve and ode45. Also, know that this feature is
powerful in that it can allow you to return variables of different sizes and types,
where attempting to pack them up to a single vector or matrix would fail. Last,
know that when we call fzero, fsolve and ode45, they were written to return
optional variables as done here.
>> mysum(17)
ans = 17
>> myapply(9)
ans = 81
Unfortunately, the converse is not always true. If you write a function with
scalar inputs in mind, it might not work on vectors. But it might! If the operators
and functions you use in the body of your function work on vectors, then your
184 Chapter 6 Functions of vectors
>> Y = myfunc(1:3)
Y = 1.3818 1.3254 1.1311
At this point, I want to take a minute to acknowledge that I have been a little
harsh in my presentation of MATLAB, because there are a number of features that
I think make life harder than it needs to be for beginners. But here, finally, we
are seeing features that show MATLAB’s strengths. MATLAB is extremely good
with matrix (and vector) operations. In fact, MATLAB’s name comes from MAtrix
LABoratory. We started the text working with scalars and for loops because I
assumed most of you had no prior programming experience. Many students
initially find thinking like a computer to be challenging, let alone thinking about
vector operations. You can always use for loops, and when in doubt, it is good
to go this route. But matrix (or vector) operations are much more efficient (both
in terms of execution time and the resulting compactness of your code) and can
make your code much easier to read.
Some of the other functions we wrote don’t work on vectors, but they can
be patched up with just a little effort. For example, here’s hypotenuse from Sec-
tion 5.5 (and Listing 5.9):
This doesn’t work on vectors because the ^ operator tries to do matrix exponenti-
ation, which only works on square matrices.
>> A = [3,5,8];
>> B = [4,12,15];
>> C = hypotenuse(A, B)
C = 5 13 17
In this case, it matches up corresponding elements from the two input vectors,
so the elements of C are the hypotenuses of the pairs (3, 4), (5, 12) and (8, 15),
respectively.
In general, if you write a function using only elementwise operators and
the function works on vectors, then the new function will also work on scalars.
Remember, to MATLAB, everything is a matrix.
i
X
Ci = Vj
j =1
In other words, the i th element of C is the sum of the first i elements from V .
MATLAB provides a function named cumsum that computes cumulative sums:
>> V = 1:5
V = 1 2 3 4 5
>> C = cumsum(V)
C = 1 3 6 10 15
Example 6.2
Write a function named cumulative_sum that uses a loop to compute the cumu-
lative sum of the input vector.
As we have seen before, when learning a new skill, writing our own functions to
perform the same operations as a built-in function is excellent practice as we can
quickly check if we get the correct answer.
186 Chapter 6 Functions of vectors
>> V = 1:5
V = 1 2 3 4 5
>> C = cumulative_sum(V)
C = 1 3 6 10 15
The inverse operation of cumsum is diff, which computes the difference be-
tween successive elements of the input vector.
>> D = diff(C)
D = 2 3 4 5
Notice that the output vector is shorter by one than the input vector. As a
result, MATLAB’s version of diff is not exactly the inverse of cumsum. If it were,
then we would expect cumsum(diff(X)) to be X:
>> cumsum(diff(V))
ans = 1 2 3 4
But it isn’t.
6.7 Products and ratios 187
Example 6.3
Write a function named mydiff that computes the inverse of cumsum, so that
cumsum(mydiff(X)) and mydiff(cumsum(X)) both return X.
Solution:
Listing 6.10 mydiff.m
1 % function res = mydiff(X)
2 %
3 % Function to compute the inverse of the cumulative sum,
4 % which is returned as res.
5 %
6 function res = mydiff(X)
7 % Y will be our vector to store the inverse cumulative sum.
8 % Initialize it with values of X.
9 Y = X;
10
11 for i=2:length(X)
12 Y(i) = Y(i)-X(i-1);
13 end
14
15 res = Y;
16
17 end
>> V = 1:5
V = 1 2 3 4 5
>> C = cumulative_sum(V)
C = 1 3 6 10 15
>> VI = mydiff(C)
VI = 1 2 3 4 5
i
Y
Pi = Vj
j =1
>> P = cumprod(V)
P = 1 2 6 24 120
Example 6.4
Write a function named cumulative_product that uses a loop to compute the
cumulative product of the input vector.
>> V = 1:5
V = 1 2 3 4 5
>> P = cumulative_product(V)
P = 1 2 6 24 120
Example 6.5
Write a function named myratio that computes the inverse of cumprod, so that
cumprod(myratio(X)) and myratio(cumprod(X)) both return X.
You can use a loop, or if you want to be clever, you can take advantage of the fact
that e ln a+ln b = ab.
If you apply myratio to a vector that contains Fibonacci numbers, you canp confirm
that the ratio of successive elements converges on the golden ratio, (1 + 5)/2 (see
Example 4.6 on page 108).
Solution:
Listing 6.12 myratio.m
1 % function res = myratio(X)
2 %
3 % Function to compute the inverse of the continuous product,
4 % which is returned as res.
5 %
6 function res = myratio(X)
7 % Y will be our vector to store the inverse continous product.
8 % Initialize it with values of X.
9 Y = X;
10
11 for i=2:length(X)
12 Y(i) = Y(i)/X(i-1);
13 end
14
15 res = Y;
16
17 end
>> V = 1:5
V = 1 2 3 4 5
>> P = cumulative_product(V)
P = 1 2 6 24 120
>> VI = myratio(P)
VI = 1 2 3 4 5
190 Chapter 6 Functions of vectors
>> VI = myratio_clever(P)
VI = 1 2 3 4 5
∃x in S : x > 0
means, “there exists some element x in the set S such that x > 0.” In MATLAB it is
natural to express this idea with a logical function, like exists, that returns 1 if
there is such an element and 0 if there is not.
Listing 6.14 exists.m
(it exists!) and we can end the function immediately without looking at the rest of
the elements.
If we exit at the end of the loop, that means we didn’t find what we were
looking for (because if we had, we would have hit the return statement).
This isn’t the only way, although it was a nice way to introduce return. I could
accomplish the same result with the following function
In this case, I initialize res with a value of 0 (false). If I find an element that is
positive, then there exists at least one element that is positive and I change the
value of res to 1 (true). Once I have found a positive element, there is no need to
continue searching since I only asked if there was at least one positive element. I
therefore add break which exits us out of the loop and hence the function. Note
that if you didn’t include break your function would still work and return the
correct result. You will just make the computer work harder than it needs to,
which is matter of efficiency.
∀x in S : x > 0
means “for all elements, x, in the set S, x > 0.”
A slightly silly way to evaluate this expression in MATLAB is to count the
number of elements that satisfy the condition. A better way is to reduce the
problem to existential quantification; that is, to rewrite
∀x in S : x > 0
as
192 Chapter 6 Functions of vectors
∼ ∃x in S : x ≤ 0
Where ∼ ∃ means “does not exist.” In other words, checking that all the ele-
ments are positive is the same as checking that there are no elements that are
non-positive.
Example 6.6
Write a function named forall that takes a vector and returns 1 if all of the
elements are positive and 0 if there are any non-positive elements.
Note that here I use return which exits us out of the function, but break in this
case would cause the same effect.
>> L = V>0
L = 0 0 0 0 1 1 1
>> find(L)
ans = 5 6 7
which indicates that elements 5, 6 and 7 have the value 1. (Remember, 1 corre-
sponds to “true”.)
If there are no “true” elements, the result is an empty vector.
>> find(V>10)
ans = Empty matrix: 1-by-0
This example computes the logical vector and passes it as an argument to find
without assigning it to an intermediate variable. You can read this version ab-
stractly as “find the indices of elements of V that are greater than 10.”
We can also use find to write exists more concisely:
Example 6.7
Write a version of forall using find.
In writing the solution, MATLAB suggests that isempty would be more efficient
than checking if a vector has a length of zero. After consulting MATLAB’s docu-
mentation, here is what the updated function would look like:
6.10 Logical vectors 195
And in the interest of exploring alternatives, we could use a logical vector instead
of find.
Not to be out done, we can use the MATLAB function any in the following version,
where here we use ∼, the logical “not” expression.
196 Chapter 6 Functions of vectors
And how did I come up with the idea of using any? MATLAB suggested its use as
a comment in forall_find_2.
>> V = -3:3
V = -3 -2 -1 0 1 2 3
Using the find command before, we found that elements 5, 6, and 7 were greater
than 0. Now imagine if the value of an element is greater than 0, we want to assign
it a value of 10. We can do this two ways. First, let’s use integer indices:
>> V(integer_index) = 10
V = -3 -2 -1 0 10 10 10
And if you had wanted to, the two statements could be combined as V(find(V>0))
= 10. Next, let’s use logical values:
>> V = -3:3;
>> logical_index = V>0
logical_index = 0 0 0 0 1 1 1
6.11 CPB Examples 197
>> V(logical_index) = 10
V = -3 -2 -1 0 10 10 10
>> V = -3:3;
>> VP = V(V>0)
VP = 1 2 3
While this example is rather trivial, we will find it much more useful in some of
our future examples.
Example 6.8
While I am certain at this point you are tired of the Antoine equation, let’s again
revisit our old friend.
a) In Example 5.8 part b) we wrote a beautiful function to compute p sat where the
user input A, B , C , the desired temperature, and the desired units. Vectorize the
function so that you can pass a vector of temperatures and return a vector of
values of p sat . To test your new function, compare to your result for Example 5.8
part c).
b) With your working function from a), test the use of the built-in find func-
tion. When computing p sat using the Antoine equation we provide a range of
temperatures, unaware beforehand what the corresponding values of p sat are.
We may desire only values of p sat below a certain value. Try using find as a
filter for this particular case. That is, check your returned p sat vector for values
less than some specified value. Then save these values and the corresponding
temperature to a pair of new vectors. Depending on the range of temperatures
you are using, try 0.5 times p sat at the highest temperature.
c) In b) you used the find function. Could you instead use a logical vector?
Remember, the use of a logical vector is preferred. The use of find in b) is an
exercise to get you accustomed to using the find command.
198 Chapter 6 Functions of vectors
To test its use, let’s compute vapor pressure in bar over the range –114.1 to 243.1
◦
C, the entire range of applicability of our Antoine equation for ethanol. In the
interest of space, we will only use a small number of temperatures.
>> a = 8.13484;
>> b = 1662.48;
>> c = 238.141;
>> tC = -114.1:30:243.1
6.11 CPB Examples 199
tC =
Columns 1 through 8
-114.1000 -84.1000 -54.1000 -24.1000 5.9000 35.9000 65.9000 95.9000
Columns 9 through 12
125.9000 155.9000 185.9000 215.9000
Nice! Know that the first two pressures are not 0. This is just an issue with
MATLAB’s chosen format.
>> p_bar(1)
ans = 7.1958e-09
b) Now we will use the find command to create a new vector with just the values
of p sat less than 0.5p sat at the highest T (since p sat increases with T ).
6.12 Glossary
top-level function: The first function in an M-file; it is the only function that can
be called from the Command Window or from another file.
helper function: A function in an M-file that is not the top-level function; it only
be called from another function in the same file.
existential quantification: A logical condition that expresses the idea that “there
exists” an element of a set with a certain property.
universal quantification: A logical condition that expresses the idea that all ele-
ments of a set have a certain property.
6.13 Exercises
Exercise 6.1 In Example 5.6 we used several functions stored in separate M-files to
compute the first n Fibonacci numbers, and then checked whether this formula produced
a Pythagorean triple for each number in the sequence. Combine all of these functions into
a single file where fib_triple.m is the top-level file. Check the function for correctness
by comparing to your results from Example 5.6. Note, do not forget about the M-file
hypotenuse, Listing 5.9.
Exercise 6.2 Revisit and modify find_negative_element from Example 6.1 to use the
find command to return the location (or index) of the first negative element in a vector.
Exercise 6.3 Let’s continue to build upon our solution to Exercises 2.1, 3.1 and 4.1. Specif-
ically, encapsulate your script from Exercise 4.1 to compute the surface tension of a fluid.
Your function should take as inputs the model parameters and the temperature. Make
sure the function is vectorized so that it can take as an input a vector of temperatures and
return a vector of surface tensions.
To confirm the correctness of your code, compute the surface tension over the entire
temperature range for which the expression is applicable for propane, ethanol, and
acetone. Compare to your results from Exercise 4.1.
Exercise 6.4 In Example 5.10 we used the ideal gas equation of state and the truncated
virial equation with corresponding states theory to estimate the molar volume of n-butane
at 350 K and 2 bar. Using the truncated virial equation, we computed the compressibility,
Z , and then calculated the molar volume as v = Z v ig , where v ig is the molar volume of
an ideal gas at the same temperature and pressure.
Since our function for the truncated virial (v_virial) is dependent on our function
for the molar volume of an ideal gas (v_ideal_gas), in Section 6.1 we decided to combine
the functions into a single file, where v_virial is the top function and v_ideal_gas is
the helper function. Let’s continue to build upon that function here. Recall that from the
NIST WebBook1 for n-butane we have: Tc = 425.125 K, p c = 37.960 bar, and ω = 0.201,
a) First, let’s vectorize v_virial and v_ideal_gas. By vectorize, you should be able to
pass a vector of temperatures and/or a vector of pressures and compute a vector of
molar volumes.
b) Test your function by computing the molar volume at a constant pressure of 2 bar
over the temperature range 350 to 450 K. How does the molar volume change with
respect to temperature at constant pressure?
c) Next, update your function so that it additionally returns the compressibility, Z . Fol-
lowing the last question, how does Z change with respect to temperature at constant
pressure?
Exercise 6.5 Let’s build upon your friction factor code from Exercise 5.2, specifically your
Moody chart. Vectorize your function and eliminate the for loop. As a hint, remember
your work from Exercise 4.4.
1 http://webbook.nist.gov/chemistry/fluid/
Chapter 7
Zero-finding
If you work through the chapter and believe these goals are not met, please
re-review the material and reach out for help.
• Each function has its own workspace, so using functions helps avoid name
collisions.
• Functions allow you to divide a large problem into small pieces, work on
the pieces one at a time, and then assemble a complete solution.
• Once you have a function working, you can forget about the details of how
it works and concentrate on what it does. This process of abstraction is an
important tool for managing the complexity of large programs.
203
204 Chapter 7 Zero-finding
Another reason you should consider using functions is that many of the tools
provided by MATLAB require you to write functions. For example, in this chapter
we will use fzero to find solutions of nonlinear equations. In the next chapter
we will use fsolve to solve systems of nonlinear equations. And later we will use
ode45 to approximate solutions to differential equations. There are many other
cases, but there is only so much class time.
7.2 Maps
In mathematics, a map is a correspondence between one set called the range and
another set called the domain. For each element of the range, the map specifies
the corresponding element of the domain.
You can think of a sequence as a map from positive integers to elements. You
can think of a vector as a map from indices to elements. In these cases the maps
are discrete because the elements of the range are countable.
You can also think of a function as a map from inputs to outputs, but in this
case the range is continuous because the inputs can take any value, not just
integers. (Strictly speaking, the set of floating-point numbers is discrete, but since
floating-point numbers are meant to represent real numbers, we think of them as
continuous.)
f (x) = x 2 − 2x − 3
which is supposed to mean that f is a function that maps from x to x 2 − 2x − 3.
The problem is that f (x) is also used to mean the value of f that corresponds to a
particular value of x. To make clear what this means, here I will instead use arrow
notation
f : x → x 2 − 2x − 3
which means “ f is the function that maps from x to x 2 − 2x − 3.” In MATLAB, this
would be expressed like this:
I’ll explain soon why this function is called error_func. Also note that I have
vectorized the function; it will work regardless if x is a scalar or vector. Vectorizing
your functions is in general a good practice. Now, back to our regularly-scheduled
programming.
>> x = 4;
>> x = sqrt(2*x+3)
x = 3.3166
>> x = sqrt(2*x+3)
x = 3.1037
206 Chapter 7 Zero-finding
>> x = sqrt(2*x+3)
x = 3.0344
>> x = sqrt(2*x+3)
x = 3.0114
>> x = sqrt(2*x+3)
x = 3.0038
After each iteration, x is closer to the correct answer, and after 5 iterations,
the relative error is about 0.1%, which is good enough for most purposes. You
could readily use a loop to automate the process.
Techniques that generate numerical solutions are called numerical methods.
The nice thing about the method I just demonstrated is that it is simple, but it
doesn’t always work as well as it did in this example, and it is not used very often
in practice. We’ll see one of the more practical alternatives in a minute.
7.5 Zero-finding
A nonlinear equation like x 2 − 2x = 3 is a statement of equality that is true for
some values of x and false for others. A value that makes it true is a solution; any
other value is a non-solution. But for any given non-solution, there is no sense of
whether it is close or far from a solution, or where we might look to find one.
To address this limitation, it is useful to rewrite nonlinear equations as zero-
finding (or root-finding) problems:
• The first step is to define an “error function” that computes how far a given
value of x is from being a solution.
In this example, the error function is
f : x → x 2 − 2x − 3
Any value of x that makes f (x) = 0 is also a solution of the original equation.
More formally, solving the equation g (x) = h (x) is equivalent to finding the
zeros (or roots) of the function f (x) = g (x) − h (x).
• The next step is to find values of x that make f (x) = 0. These values are
called zeros of the function, or equivalently roots.
Zero-finding lends itself to numerical solution because we can use the values of
f , evaluated at various values of x, to make reasonable inferences about where to
look for zeros.
For example, if we can find two values x l and x r such that f (x l ) > 0 and
f (x r ) < 0, and f is continuous, then per the intermediate value theorem we can
7.6 fzero 207
be certain that there is at least one zero between x l and x r . In this case we would
say that x l and x r bracket a zero. This is the basis of bracketing methods of root
finding. Graphically this scenario might look like fig. 7.1.
7.6 fzero
fzero is a built-in MATLAB function that combines the best features of several ef-
ficient and robust numerical methods. Specifically, fzero is a bracketing method
of root finding that uses bisection, secant, and inverse quadratic interpolation
methods.
In order to use fzero, you have to define a MATLAB function that computes
the error function you derived from the original nonlinear equation, and you have
208 Chapter 7 Zero-finding
to provide two values that bracket the location of a zero or an initial guess of the
location of a zero; if you provide an initial guess, it is actually used to find brackets.
Providing brackets is preferred, but often an initial guess is more convenient.
We’ve already seen an example of an error function in Listing 7.1:
You can call error_func from the Command Window , and confirm that there are
zeros at 3 and -1.
>> error_func(3)
ans = 0
>> error_func(-1)
ans = 0
But let’s pretend that we don’t know exactly where the roots are; we only know
that one of them is near 4. Then we could call fzero like this:
>> fzero(@error_func, 4)
ans = 3
Alternatively, if you know two values that bracket the root, you can provide
both:
The second argument here is actually a vector that contains two elements. The
7.7 What could go wrong? 209
Not surprisingly, it starts by computing f (2) and f (4) to confirm that they in
fact bracket a zero. After each iteration, the interval that brackets the root gets
smaller; fzero stops when the interval is so small that the estimated zero is correct
to 16 digits, 2.2204 × 10−16 to be exact. If you don’t need that much precision, you
can tell fzero to give you a quicker, dirtier answer (see the documentation for
details about changing the tolerance).
Which is a very confusing error message. The problem is that MATLAB treats
the first argument as a function call, so it calls error_func with no arguments.
Since error_func requires one argument, the message indicates that the input
argument is “undefined,” although it might be clearer to say that you haven’t
provided a value for it.
Another common problem is writing an error function that never assigns a
value to the output variable. In general, functions should always assign a value to
the output variable, but MATLAB doesn’t enforce this rule, so it is easy to forget.
For example, if you were to re-write Listing 7.1:
210 Chapter 7 Zero-finding
>> error_func(4)
y = 5
It looks like it worked, but don’t be fooled. This function assigns a value to y,
and it displays the result, but when the function ends, y disappears along with
the function’s workspace. If you try to use it with fzero, you get
If you read it carefully, this is a pretty good error message (with the quibble
that “output argument” is not a good synonym for “output variable”).
You would have seen the same error message when you called error_func
from the Command Window , if only you had assigned the result to a variable:
>> x = error_func(4)
y = 5
You can avoid all of this if you remember these two rules:
• When you call a function, you should always do something with the result
(either assign it to a variable or use it as part of an expression, etc.).
7.8 Finding an initial guess 211
When you write your own functions and use them yourself, it is easy for t Well, okay, there are ex-
mistakes to go undetected. But when you use your functions with MATLAB ceptions to the rule that
functions like fzero, you have to get it right! functions should always
Yet another thing that can go wrong: if you provide an interval for the initial assign values to their
output variables, includ-
guess and it doesn’t actually contain a root, you get
ing find_triples (List-
ing 5.15). Functions that
>> fzero(@error_func, [0,1]) don’t return a value are
sometimes called “com-
Error using fzero (line 274) mands,” because they do
The function values at the interval endpoints must differ in something (like display val-
sign. ues or generate a figure) but
either don’t have an output
variable or don’t make an
Or it may be that the interval contains a root, it is just not guaranteed per the assignment to it.
intermediate value theorem. This is where understanding the basis of fzero is
beneficial.
There is one other thing that can go wrong when you use fzero, but this
one is less likely to be your fault. It is possible that fzero won’t be able to find a
root. fzero is generally pretty robust, so you may never have a problem, but you
should remember that there is no guarantee that fzero will work, especially if
you provide a single value as an initial guess. Even if you provide an interval that
brackets a root, things can still go wrong if the error function is discontinuous.
Remember the basis of bracketing methods. If your function is continuous
and changes signs over an interval, then at least one zero exists.
12
10
-2
-4
-2 -1 0 1 2 3 4 5
12
10
-2
-4
-2 -1 0 1 2 3 4 5
As this example shows you, fplot calls your function several times, so you
will probably want to make sure your function is silent before you plot.
All of the other tricks we used before with plot work here too. For example,
you can add a title and axis labels, and you can save and print your figure. In
general, when using fplot to obtain estimates of location of zeros, we will not
worry too much about making the plot pretty. But fplot is a very convenient
function, and you may find it useful for other applications.
Last, students often find it helpful to plot a reference y = 0 line to help identify
zeros. This can readily be accomplished. Here’s an example where the reference
line is plotted as dotted black line:
>> hold on
>> fplot(@error_func, [-2,5])
>> plot([-2,5],[0,0])
12
10
-2
-4
-2 -1 0 1 2 3 4 5
12
10
-2
-4
-2 -1 0 1 2 3 4 5
>> sin = 3;
>> x = 5;
>> sin(x)
Index exceeds matrix dimensions.
In this example, the problem is clear. Since the value of sin is a scalar, and a
scalar is really a 1×1 matrix, MATLAB tries to access the 5th element of the matrix
and finds that there isn’t one. Of course, if there were more distance between the
assignment and the “function call,” this message would be pretty confusing.
But the only thing worse than getting an error message is not getting an error
message. If the value of sin was a vector, or if the value of x was smaller, you
would really be in trouble.
7.9 More name collisions 215
>> sin = 3;
>> sin(1)
ans = 3
>> f = f+1
Error: "f" previously appeared to be used as a function or
command, conflicting with its use here as the name of a variable.
A possible cause of this error is that you forgot to initialize the
variable, or you have initialized it implicitly using load or eval.
At least, that’s what you get if you are lucky. If this happens inside a function,
MATLAB tries to call f as a function, and you get this
There is no universal way to avoid these kind of collisions, but you can improve
your chances by choosing variable names that don’t shadow existing functions,
and by choosing function names that you are unlikely to use as variables. That’s
why in Section 7.3 I called the error function error_func rather than f. I often
give functions names that end in func, so that helps too.
This warning came up previously when discussing variable names. The MAT-
LAB documentation page “Variable Names” recommends the use of the exist
function if you are unsure. exist returns 0 if there are no existing variables,
functions, or other artifacts with the proposed name. For example:
or equivalently
>> exist('test_func')
ans = 0
216 Chapter 7 Zero-finding
reading: Examine your code, read it back to yourself, and check that it means
what you meant to say.
ruminating: Take some time to think! What kind of error is it: syntax, run-time,
logical? What information can you get from the error messages, or from the
output of the program? What kind of error could cause the problem you’re
seeing? What did you change last, before the problem appeared?
retreating: At some point, the best thing to do is back off, undoing recent changes,
until you get back to a program that works, and that you understand. Then
you can starting rebuilding.
your program into another file before you start stripping it down. Then you can
paste the pieces back in a little bit at a time.
To summarize, here’s the Ninth Theorem of debugging:
7.11 Examples
Example 7.1
(a) Write a function called cheby6 that evaluates the 6th Chebyshev polynomial.
It should take an input variable, x, and return
(b) Use fplot to display a graph of this function in the interval from 0 to 1.
Estimate the location of any zeros in this range.
(c) Use fzero to find as many different roots as you can. Does fzero always
find the root that is closest to the initial guess?
Solution: (Link to screen cast and accompanying M-file.) Let’s begin by creating
the M-file cheby6.
To get an estimate of the locations of the roots over the interval 0 to 1, let’s use
fplot.
>> hold on
>> fplot(@cheby6,[0,1])
>> title('cheby6')
>> plot([0,1],[0,0],':k')
218 Chapter 7 Zero-finding
cheby6
1
0.8
0.6
0.4
0.2
-0.2
-0.4
-0.6
-0.8
-1
0 0.2 0.4 0.6 0.8 1
We appear to have roots near 0.2, 0.7, and 0.9. Let’s use fzero with these initial
guesses to find them.
Lastly, we are asked if fzero always returns the root closest to the initial guess.
The key is to be careful. Here’s an example:
>> fzero(@cheby6,0.85)
ans = 0.9659
>> fzero(@cheby6,0.84)
ans = 0.7071
>> fzero(@cheby6,0.49)
ans = 0.7071
>> fzero(@cheby6,0.48)
ans = 0.2588
A small change in the guess causes you to find a different root. Remember that
7.11 Examples 219
MATLAB uses the guess to find brackets of the root. This is one of the main reason
I said that specifying a bracket is preferred, while specifying an initial condition
is typically more convenient. When we have multiple roots, if I specify a bracket
around the root of interest, I will be certain to obtain that root. When I specify an
initial guess, it is not clear to me how MATLAB searches for a bracket.
Example 7.2
The density of a duck, ρ d , is 0.3 g/cm3 (0.3 times the density of water, ρ w = 1
g/cm3 ). The volume of a sphere with radius r is 43 πr 3 . If a sphere with radius r is
submerged in water to a depth d , the volume (V ) of the sphere below the water
line is
π
V= (3r d 2 − d 3 ) as long as d < 2r
3
An object floats at the level where the weight of the displaced water equals the
total weight of the object. Assuming that a duck is a sphere with radius 10 cm, at
what depth does a duck float?
Here are some suggestions about how to proceed:
Solution:
Let’s begin by writing an expression for the weight of the duck (Wd ) and the weight
of the displaced water (Ww )
4 4
Wd = ρ d g V = ρ d g πr 3 = 0.3ρ w g πr 3 (7.2)
3 3
π
Ww = ρ w g V = ρ w g (3r d 2 − d 3 ) (7.3)
3
4 π
0.3ρ w g πr 3 = ρ w g (3r d 2 − d 3 ) (7.4)
3 3
which simplifies to
We know that r = 10 cm, so that the only unknown is d , which you can think of as
our x in this case. (The units of d will agree with r .)
Next, let’s create a function called duck that we will pass to fzero.
To get an estimate of the locations of the roots over the interval 0 to 2r = 20 cm,
let’s use fplot.
>> hold on
>> fplot(@duck,[0,20])
>> title('duck')
>> plot([0,20],[0,0],':k')
7.12 Functions: what we’ve done so far 221
duck
1500
1000
500
-500
-1000
-1500
-2000
-2500
-3000
0 5 10 15 20
It looks like we only have one solution, which is near d = 8 cm. Let’s use this as our
initial guess and have fzero find the root.
>> d = fzero(@duck,8)
d = 7.2651
Nice! So for our spherical duck with a radius of 10 cm (or diameter of 20 cm),
7.2651 cm is below the surface of the pond. This satisfies the constraint that d < 2r .
For a buoyancy problem, we expect there only to be one unique solution. So for
problems of this type, you might instead specify the range of all possible values.
>> d = fzero(@duck,[0,20])
d = 7.2651
each function has its own workspace, and that inputs and outputs are defined
carefully to avoid unexpected interactions. Additionally, for large calculations we
have seen that they allow us to modulate our code, making our code both more
readable and easier to debug.
We have seen so far that fzero and fplot both expect a function with just a
single input variable. But what if we wish to pass an extra parameter? This can be
accomplished using an anonymous function, which we will discuss here.
Additionally, the very first section of this text, Section 1.1, is titled “A glorified
calculator”. There are many times where you will want to obtain an answer to a
problem quickly. Using MATLAB as a calculator in these cases is desirable. In this
chapter we will additionally show how fzero and fplot can be used in calculator
mode with the help of anonymous functions. I do, however, recommend writing
script and function M-files whenever possible. This will allow you to document
and save your work.
t There are two additional
techniques that can be used
to pass extra parameters. 7.13 Anonymous function
One is the use of nested
functions. We will use An anonymous function is a function that is not stored in a file, but is instead
nested functions later in stored as a variable (a function_handle). An anonymous function behaves just
the course, and I would like a function stored in a file, except it can only contain a single executable
suggest you avoid them for
statement.
now as we are just getting
Imagine that you wish to evaluate the cube root of a number. You know this
started. With nested func-
tions the workspace rules is a calculation you will need to perform often, so you decide to write a function
change slightly, which is M-file. This leads to
why I suggest to wait. The
other is to use global vari- Listing 7.4 cubert.m
ables. However, as MATLAB
notes, global variables carry 1 function res = cubert(X)
noticeable risks, and should 2 res = X.^(1/3);
be used sparingly, if at all. 3 end
As a note, GNU Octave does
not support nested func-
tions, but does support Then from the Command Window
anonymous functions and
global variables.
>> cubert(9)
ans = 2.0801
>> cubert(27)
ans = 3
Beautiful! Now imagine that this is a calculation you will need to perform often,
but just within this session, and maybe you are not using your own computer
so you do not wish to (or can not) save a file to the computer. Or you are in the t Remember a script file is
middle of an exam and do not have the time to write a function file! We can nothing more than a doc-
ument containing a series
instead create an anonymous function in the Command Window . Repeating the
of commands you would
same calculations: otherwise execute in the
Command Window . When
>> crt = @(X) X.^(1/3); we run a script, we execute
commands line-by-line as
>> crt(9) if we had entered them in
ans = the Command Window . One
could therefore create a
2.0801
script that contains anony-
mous functions to save your
>> crt(27) work. Anonymous func-
ans = tions could also be used in
3 function files.
Notice that in the assignment statement for crt, the first term on the right is a han-
dle, @; the variable therefore corresponds to a function handle. The parentheses
immediately following the handle indicate the input variable(s).
Just as with function files, anonymous functions can have more than a single
input variable. To compute the length of the hypotenuse of a triangle:
>> hyp(3,4)
ans =
5
The answer can also be stored to a new variable rather than the default ans which
will be overwritten the next calculation:
>> hyp = @(A,B) sqrt(A.^(2)+B.^(2));
where a = 4, b = 2 and c = 1, over the range 0 to 2π. Let’s start using what we have
done so far and create a function M-file.
Then
>> fplot(@sin_fun,[0,2*pi])
>> a = 4; b = 2; c = 1;
>> sfun = @(X) a.*sin(b.*X)+c;
>> fplot(sfun,[0,2*pi])
-1
-2
-3
0 1 2 3 4 5 6
Notice that when using fplot to plot our anonymous function, we do not need
@ in the function call. This is because the anonymous function, here sfun, is
already of type function handle.
Another use of anonymous functions is to quickly find the zeroes of a function.
For example, find where y = 4 sin(2x) + 1 is equal to zero over the range 0 to 2. I
will confirm the solution by also using our function file.
>> sin_fun(2)
ans =
-2.0272
>> sfun(2)
ans =
-2.0272
A perfect match! Now what if instead we wanted to change the parameter c from
1 to 2. For the case of our function file, I will create a copy and save as sin_fun_2,
where I change the value of c.
And evaluating
>> sin_fun_2(2)
ans =
-1.0272
>> c = 2;
>> sfun(2)
ans =
-2.0272
>> c = 2;
>> sfun = = @(X) a.*sin(b.*X)+c;
>> sfun(2)
ans =
-1.0272
simulating a new session by clearing all variables after saving the anonymous
function. At each step I will evaluate the function at x = 2.
>> sfun(2)
ans =
-1.0272
>> load('sfun.mat')
>> sfun(2)
ans =
-1.0272
This is NOT generally something I find myself using, but I thought I would
mention it for completeness. I typically prefer to write scripts and save my work
in human-readable form.
4 π
0.3g πr 3 = ρg (3r d 2 − d 3 ) (7.7)
3 3
Simplifying
1 % res = duck2(x,r,rho)
2 % Function to find the required depth of a duck to remain
3 % buoyant in water. duck2 requires that we pass parameters
4 % r (radius of duck) and rho (density of the liquid), in
5 % addition to the depth (x) which is what we will solve for.
6 function res = duck2( x,r,rho )
7 res = 0.3.*4.*r.^(3)-rho.*(3.*r.*x.^(2)-x.^(3));
8 end
Next, let’s verify that we get the same solution as before with duck
>> d0 = 7;
>> dref = fzero(@duck,d0)
dref = 7.2651
>> r = 10;
>> rho = 1;
>> duck_zero = @(X) duck2(X,r,rho);
>> d = fzero(duck_zero,d0)
d = 7.2651
A perfect match! If you wanted to look at different values of r and ρ, you would
update their values, re-define the anonymous function, and then re-solve using
fzero. Cool!
After seeing this example, think of how you might be able to use anonymous
functions to generalize the Antoine equation functions you have already written?
We could write a function to perform an Antoine equation calculation for the
general case in which we pass the temperature in addition to parameters A, B , and
C . This is great in that the function can be used for any fluid. But it is annoying
in that if for a given problem I am only interested in a single fluid, say ethanol,
I need to pass the same values of A, B , and C every call. I could instead use an
anonymous function to pass A, B , and C , to create an ethanol-specific function
for the problem at hand. This is a good practice because if I know my general
code is bug-free, I can rest assured my anonymous functions is bug-free too, and
it can streamline the calculations.
7.17 Numerical methods: zero-finding 229
1. Choose a value of x l and x r (the range of the search) such that the func-
tion changes sign over the interval. Put differently, x l and x r should have
different signs such that f (x r ) f (x l ) < 0.
1
x ans = (x l + x r )
2
3. Evaluate f (x ans ).
(a) If f (x r ) f (x ans ) < 0, then the root lies in this new sub-interval. Set
x l = x ans and return to Step 2.
(b) Else if f (x l ) f (x ans ) < 0, then the root must lie in this new sub-interval.
Set x r = x ans and return to Step 2.(Note, you could just as well check if
f (x r ) f (x ans ) > 0.)
(c) Else if f (x ans ) = 0, we have found our answer, terminate the calcula-
tion. (Note, since anything times anything is 0, you could just as well
check it f (x r ) f (x ans ) = 0.)
230 Chapter 7 Zero-finding
1 % res = bisection_zero(fcn_handle,Bracket,maxit,tol)
2 % Function to perform bisection method to find the root
3 % of a function (evaluated/provided in fcn_handle)
4 % in the range xl to xr. maxit sets the maximum number
5 % of iterations and tol sets the tolerance for convergence.
6 % Bracket is a vector where the first element is xl and the
7 % second element is xr.
8 function res = bisection_zero(fcn_handle,Bracket,maxit,tol)
9 %hold on
10 % Start by un-packing Bracket into x1 and xr
11 xl = Bracket(1);
12 xr = Bracket(2);
13 % Check to make sure f(xl) and f(xr) have opposite signs.
14 % If not, don't do anything.
15 if fcn_handle(xl)*fcn_handle(xr) > 0
16 %f(xl) and f(xr) have the same sign
17 disp('f(xl) and f(xr) have the same sign. Exit.')
18 % else, f(xl) and f(xr) have the opposite sign,proceed
19 else
20 for i=1:maxit
21 % Estimate location of zero as midpoint between
22 % xl and xr
23 xans = 0.5*(xl+xr);
24 fxans = fcn_handle(xans);
25 fxl = fcn_handle(xl);
26 fxr = fcn_handle(xr);
27 % Check if we found our root
28 if fxans^(2) < tol^(2)
29 res = xans;
30 %i
31 break
32 % Otherwise, update our brackets
33 elseif fxl*fxans < 0
34 xr = xans;
35 elseif fxr*fxans < 0
36 xl = xans;
37 else
38 % We should never get here, but just in case
39 disp('Critical error. Exit')
40 disp('Current estimate:')
41 xans
42 fxans
43 break
232 Chapter 7 Zero-finding
44 end
45 % Check to see if we reached the maximum number of
46 % iterations. If we did, print the current estimates.
47 if i == maxit
48 disp('Reached maximum number of iterations. Exit')
49 disp('Current estimate:')
50 xans
51 fxans
52 end
53 %plot(i,xans,'ro')
54 end
55 end
56 end
Notice the variable fcn_handle and its use in the function. Just as we use a
function handle to tell fzero which function to find the roots of, we can use a
function handle here too.
Now let’s see it in action.
>> xl = 0;
>> xr = 20;
>> tolerance = 1e-12;
>> maxiter = 1000;
>> bisection_zero(@duck,[xl,xr],maxiter,tolerance)
ans = 7.2651
This is exactly what we had before. If you uncomment lines 9 and 53, you can
generate a plot of the estimate of the root location versus the iteration number,
which is shown below.
7.17 Numerical methods: zero-finding 233
10
9.5
8.5
7.5
6.5
5.5
5
0 10 20 30 40 50
Figure 7.9 The current estimate of the root location versus the iteration
number for our duck buoyancy problem using bisection_zero.
Let’s walk through what MATLAB is doing step-by-step. Let’s start by revisiting
fig. 7.1, which is for our duck problem, which is shown again in the margin as Figure 7.10 Our initial estimate of
x l and x r that bracket our zero.
fig. 7.10 for convenience. We begin with x l = 0 and x r = 20, which bracket our
zero, with the zero we are searching for located where the red lines cross. Using
bisection method, our guess of the zero location each iteration is the midpoint
between x l and x r . Our first guess then is x ans = 10.
234 Chapter 7 Zero-finding
The interval 5 to 10 then gets cut in half, and our third guess of the zero
location is 7.5. At this point the range over which we are searching is 2.5, 8 times
smaller than the original range of 20. The refinement continues, and we see that
by the 10th iteration we appear to be very close to our final answer.
Next, let’s see what happens if we provide a value of x r < 7.2651 and then a
small value for the maximum number of iterations.
>> bisection_zero(@duck,[xl,5],maxiter,tolerance)
f(xl) and f(xr) have the same sign. Exit.
>> bisection_zero(@duck,[xl,xr],4,tolerance)
Reached maximum number of iterations. Exit
Current estimate:
xans = 6.2500
fxans = 272.2656
The last point I would like to make is if I uncomment line 30, I can see how many
iterations it takes to find the root. We are using format short, so our answer
only contains four decimal places. If I set the tolerance to 1×10−4 , I get our answer
after 25 iterations. As the tolerance becomes smaller, the number of interactions
increases. For a tolerance of 1 × 10−12 , we require 51 iterations. While this is not
a problem here, it is something to keep in mind if you have a more complicated
problem.
Last, before moving on, it is useful to demonstrate that we can more properly
extract additional information by returning additional/optional variables. I will
save a copy of the function and demonstrate how we can return the number of
iterations and the value of the error function at the solution.
236 Chapter 7 Zero-finding
1 % res = bisection_zero_2(fcn_handle,Bracket,maxit,tol)
2 % Function to perform bisection method to find the root
3 % of a function (evaluated/provided in fcn_handle)
4 % in the range xl to xr. maxit sets the maximum number
5 % of iterations and tol sets the tolerance for convergence.
6 % Bracket is a vector where the first element is xl and the
7 % second element is xr.
8 function [res,i,fxans] = bisection_zero_2(fcn_handle,Bracket,maxit,tol)
9 %hold on
10 % Start by un-packing Bracket into x1 and xr
11 xl = Bracket(1);
12 xr = Bracket(2);
13 % Check to make sure f(xl) and f(xr) have opposite signs.
14 % If not, don't do anything.
15 if fcn_handle(xl)*fcn_handle(xr) > 0
16 %f(xl) and f(xr) have the same sign
17 disp('f(xl) and f(xr) have the same sign. Exit.')
18 % else, f(xl) and f(xr) have the opposite sign,proceed
19 else
20 for i=1:maxit
21 % Estimate location of zero as midpoint between
22 % xl and xr
23 xans = 0.5*(xl+xr);
24 fxans = fcn_handle(xans);
25 fxl = fcn_handle(xl);
26 fxr = fcn_handle(xr);
27 % Check if we found our root
28 if fxans^(2) < tol^(2)
29 res = xans;
30 %i
31 break
32 % Otherwise, update our brackets
33 elseif fxl*fxans < 0
34 xr = xans;
35 elseif fxr*fxans < 0
36 xl = xans;
37 else
38 % We should never get here, but just in case
39 disp('Critical error. Exit')
40 disp('Current estimate:')
41 xans
42 fxans
43 break
7.17 Numerical methods: zero-finding 237
44 end
45 % Check to see if we reached the maximum number of
46 % iterations. If we did, print the current estimates.
47 if i == maxit
48 disp('Reached maximum number of iterations. Exit')
49 disp('Current estimate:')
50 xans
51 fxans
52 end
53 %plot(i,xans,'ro')
54 end
55 end
56 end
>> xl = 0;
>> xr = 20;
>> tolerance = 1e-12;
>> maxiter = 1000;
>> d = bisection_zero(@duck,[xl,xr],maxiter,tolerance)
d = 7.2651
>> d = bisection_zero_2(@duck,[xl,xr],maxiter,tolerance)
d = 7.2651
Figure 7.13 Finding our first guess of x ans for our duck buoyancy problem
using linear interpolation.
If the function is in fact linear over the region x l to x r , we obtain the root of the
function. Eventually the interval becomes so small that the linear approximation
is very good. Just like the bisection method, convergence is guaranteed so long as
your initial estimates of x l and x r bracket a zero and your function is continuous
over the range. The hope is that with an improved estimation technique, the
number of iterations required for convergence decreases.
If you need help connecting f (x l ) and f (x r ) with a straight line, have a look at
the problem statement for Example 1.7 on page 19. Let’s work out the expression
we will need to generate this new estimate. Assuming f (x l ), g (x ans ) and f (x r ) are
co-linear, where here I use g (x ans ) to designate the straight line connecting f (x l )
to f (x r ) evaluated at x ans :
f (x l ) − g (x ans ) g (x ans ) − f (x r )
=
x l − x ans x ans − x r
At our new estimate of the root x ans , g (x ans ) = 0 so that:
f (x l ) − f (x r )
=
x l − x ans x ans − x r
Solving for x ans we find:
x r f (x l ) − x l f (x l )
x ans =
f (x l ) − f (x r )
Updating our bisection_zero_2 function for the linear interpolation method
we have:
7.17 Numerical methods: zero-finding 239
1 % res = linear_interpol_zero(fcn_handle,xl,xr,maxit,tol)
2 % Function to perform linear interpolation method to find the root
3 % of a function (evaluated/provided in fcn_handle)
4 % in the range xl to xr. maxit sets the maximum number
5 % of iterations and tol sets the tolerance for convergence.
6 % Bracket is a vector where the first element is xl and the
7 % second element is xr.
8 function [res,i,fxans] = linear_interpol_zero(fcn_handle,Bracket,maxit,tol)
9 %hold on
10 % Start by un-packing Bracket into x1 and xr
11 xl = Bracket(1);
12 xr = Bracket(2);
13 % Check to make sure f(xl) and f(xr) have opposite signs.
14 % If not, don't do anything.
15 if fcn_handle(xl)*fcn_handle(xr) > 0
16 %f(xl) and f(xr) have the same sign
17 disp('f(xl) and f(xr) have the same sign. Exit.')
18 % else, f(xl) and f(xr) have the opposite sign,proceed
19 else
20 for i=1:maxit
21 fxl = fcn_handle(xl);
22 fxr = fcn_handle(xr);
23 % Find our new guess of the root location
24 xans = (xr*fxl-xl*fxr)/(fxl-fxr);
25 % Evaluating our funciton at this new estimate of the root
26 fxans = fcn_handle(xans);
27 % Check if we found our root
28 if fxans^(2) < tol^(2)
29 res = xans;
30 %i
31 break
32 elseif fxl*fxans < 0
33 xr = xans;
34 elseif fxr*fxans < 0
35 xl = xans;
36 else
37 disp('Critical error. Exit')
38 break
39 end
40 if i == maxit
41 disp('Reached maximum number of iterations')
42 xans
43 fxans
240 Chapter 7 Zero-finding
44 end
45 %plot(i,xans,'ro')
46 end
47 end
48 end
>> xl = 0;
>> xr = 20;
>> tolerance = 1e-12;
>> maxiter = 1000;
>> [d,niter,fans] = bisection_zero_2(@duck,[xl,xr],maxiter,tolerance)
d = 7.2651
niter = 51
fans = -4.5475e-13
And with just this small improvement, the number of iterations decreases from
51 to 9 while obtaining the same solution.
7.18 roots
The function fzero is very robust and can be used to find the roots of any linear
or non-linear equation. For example,
t Notice how I used a func-
tion handle to refer to MAT- >> fzero(@sin,[1,4])
LAB’s built-in function sin.
ans = 3.1416
π, just as we expected.
Most often, we tend to encounter polynomials. For example, cubic equations
of state, routinely used to predict the phase behavior of fluid systems, are cubic
equations. It would be great if MATLAB had a function to find all of the roots of
an equation. Otherwise, I would need to use fplot to estimate the range of each
root, and then apply fzero for each root. This was exactly the case in Example 7.1.
To find all of the roots of a polynomial, we can use MATLAB’s built-in function
roots. I stress, this is only for polynomials. Also, notice that I did not mention
that we would find all of the roots over a particular range. MATLAB will return
all of the roots; it does not allow you to specify a range. You will therefore have
to manually check which of the roots are within the desired range. With roots,
MATLAB does not use a bracketing technique. Instead, it uses algebraic tricks,
7.18 roots 241
which are beyond the scope of this class. If you are interested, have a look at the
freely available material from the textbook “Numerical Methods with Applications”
at the University of South Florida, specifically the material on the “Solution of
Quadratic Equations” followed by the “Solution of Cubic Equations.” And if you
are really interested, have a look at “Tea Time Numerical Analysis” in the Open
Texbook Library.
Imagine we have a polynomial of the form C (1)x N +C (2)x N −1 + ... +C (N )x +
C (N + 1) = 0. You need to create a vector C with all of the coefficients. Then to
find the roots, use roots(C). Let’s apply it to the 6th Chebyshev polynomial in
Example 7.1. It is a 6th order polynomial, so we can have up to 6 roots. To begin,
write your function just as you would for fzero. For this case, we have
Within the range 0 to 1, we have 0.9659, 0.7071, and 0.2588. This agrees perfectly t For now, don’t worry about
with what we found using fzero. Formally, while C is a row vector (1 × 7), Sol is a the difference between a
column vector (6×1). So to select an element from Sol, we would use a command row and column vector.
like Both are vectors, so you can
select an element using a
single index. We will revisit
>> zero_1 = Sol(3)
row and column vectors
zero_1 = 0.9659 later.
or equivalently
242 Chapter 7 Zero-finding
7.19 Examples
Example 7.3
Use fzero to determine the first nontrivial root of sin(x) = x 2 , where x is in radians.
(By nontrivial, I mean x > 0.)
sin(x) − x 2 = 0
Before using fzero, let’s use fplot to estimate the location of the desired root. We
know that sin(x) is bound between 0 and 1. Therefore, the desired root must be
over the range 0 ≤ x ≤ 1. Use this as our bounds for the plot:
>> hold on
>> fplot(@sin_error_func,[0,1])
>> plot([0,1],[0,0],':k')
7.19 Examples 243
0.25
0.2
0.15
0.1
0.05
-0.05
-0.1
-0.15
-0.2
0 0.2 0.4 0.6 0.8 1
The non-trivial root (x 6= 0) appears to be between 0.8 and 1.0. Using fzero over
this range:
>> fzero(@sin_error_func,[0.8,1])
ans = 0.8767
Example 7.4
Use fzero to determine the real root of ln x 2 = 7. Hint: start by using fplot to
look at your function over the range 0 to 40.
ln x 2 − 7 = 0
Before using fzero, let’s use fplot to estimate the location of the desired root. It
is suggested that we look over the range 0 to 40.
>> fplot(@log_error_func,[0,40])
-2
-4
-6
-8
-10
-12
0 5 10 15 20 25 30 35 40
The root appears to be between 30 and 40. Using fzero over this range:
>> fzero(@log_error_func,[30,40])
ans = 33.1155
From the plot, I might have instead estimated the lower-bound of the range to be
35. Had I done this, I would receive the following error message:
>> fzero(@log_error_func,[35,40])
After our discussion of the bisection method, this error should now make more
sense to you.
7.19 Examples 245
Example 7.5
Determine the real roots of:
a) Use fzero to find each root. Note, we have a 5th order polynomial, so we can
have up to 5 roots.
b) Use roots to find the roots. Please first try to solve a). This way you can better
understand the difference of between fzero and roots.
Before using fzero, let’s use fplot to estimate the location of the desired root.
Since we have a fifth order polynomial, can have up to five real roots. Since we
don’t have a good idea of the roots, let’s look at the extreme interval of –1000 to
1000. Using fplot with these bounds we have:
>> fplot(@poly_error_func,[-1000,1000])
246 Chapter 7 Zero-finding
10 14
6
-2
-4
-6
>> fplot(@poly_error_func,[0,1])
-5
-10
-15
-20
-25
0 0.2 0.4 0.6 0.8 1
the root:
>> fzero(@poly_error_func,[0,1])
ans = 0.5793
b) Next, let’s use the roots function. Remember, fzero is a robust function that
may be applied to any non-linear equation. On the other hand, roots only
applies to polynomials. The added benefit of roots is that it returns all of the
roots from one call, both real and imaginary.
We start by creating a vector of coefficients starting from the highest power in x
(the n th power) down to the 0th power. (Remember, x 0 = 1, so the 0th power is
the term without an x.)
Now we can use the roots function which takes as an argument the vector of
coefficients.
>> roots(C)
ans =
5.5561 + 2.3338i
5.5561 - 2.3338i
1.0773 + 0.8606i
1.0773 - 0.8606i
0.5793 + 0.0000i
We have just one real root, x = 0.5793. This agrees exactly with the result of
fzero.
Example 7.6
Going all the way back to Example 1.5 on page 15, you were asked to calculated the
normal boiling point of ethanol using Antoine’s equation. We did this using eq. (1.3)
where we analytically solved Antoine’s equation for T . Doing so we calculated the
normal boiling point of ethanol to be T = 78.289◦ C.
Now armed with fzero, we no longer need to perform the algebra to solve for T .
Using the original form of the Antoine equation, eq. (1.2), use fzero to calculate
the normal boiling point of ethanol. Does you answer agree with the analytic
solution?
In your error function, code in the Antoine parameters and pressure. This leaves T
as the only unknown. Also try using an anonymous function and check that you
obtain the same answer.
248 Chapter 7 Zero-finding
Solution:
All the way back in Example 1.5, we analytically solved Antoine’s equation for T and
used this to calculated the normal boiling point of ethanol. Doing so we found the
normal boiling point to be T = 78.289 ◦ C. Here, we are asked to solve numerically
using fzero. When learning a new function such as fzero, testing on problems
for which you know the answer is always a good idea.
The Antoine equation takes the form:
B
log10 p sat = A −
T +C
Next, we need to re-write Antoine’s equation as an error function suitable for use
with fzero:
B
log10 p sat − A + =0
T +C
Now we can write our MATLAB error function. In the function file I will define
p = 760 mmHg, along with the Antoine parameters. I will minimize comments as
we have seen Antoine’s equation all too much by this point.
A perfect match with our analytic solution! Nice! So if you encounter a challenging
problem in the future, why not give MATLAB a try? There is no need for an analytic
solution. Also, note that while here I provide an initial guess, we know physically
that Antoine’s equation is single valued. We therefore could have just specified the
entire temperature range as a bracket and would have obtained the same result.
In the interest of fun and incremental development, let’s keep building up our
example. Rather than specify the desired pressure within the error function, let’s
add it as an input variable so that the user can readily change the pressure and
compare the corresponding temperature at saturation. The challenge with doing
this is fzero expects a function with a single input and a single output variable.
7.21 Glossary 249
We can pass this additional information to the error function using an anonymous
function. Here is what that might look like.
The same answer. Great! If you wished, you could further generalize the error
function by additionally taking your Antoine parameters as input variables.
7.21 Glossary
analytic solution: A way of solving an equation by performing algebraic opera-
tions and deriving an explicit way to compute a value that is only known
implicitly.
map: A correspondence between the elements of one set (the range) and the
elements of another (the domain). You can think of sequences, vectors and
functions as different kinds of maps.
discrete set: A set, like the integers, whose elements are countable.
continuous set: A set, like the real numbers, whose elements are not countable.
You can think of floating-point numbers as a continuous set.
250 Chapter 7 Zero-finding
anonymous function: A function that is not stored in a file, but is instead stored
as a variable (a function_handle). It behaves just like a function stored in
a file, except it can only contain a single executable statement.
MAT-file: A file containing MATLAB formatted data. We can load data from or
write date to these files using the load and save functions, respectively.
7.22 Exercises 251
7.22 Exercises
Exercise 7.1 Air under normal conditions flows through a 4 mm diameter pipe with an
average velocity of V = 60 m/s. We wish to determine the pressure drop in a 0.2 m section
of the pipe.
For air under normal conditions, the density and viscosity can be taken to be ρ = 1.23
kg/m3 and µ = 1.79 × 10−5 N·s/m2 . The pressure drop (∆p) may be computed as
l 1
∆p = f ρV 2 (7.11)
D2
where l is the length of the pipe, D is the pipe diameter, ρ is the density of the fluid (air),
V is the fluid (air) velocity, and f is the friction factor. For this problem we have turbulant
flow. The turbulant portion of the Moody chart is represented by the Colebrook formula
à !
1 ²/D 2.51
p = −2 log10 + p (7.12)
f 3.7 Re f
where ² is the pipe roughness, and Re is the Reynold’s number, defined as
ρV D
Re = (7.13)
µ
Compute the pressure drop for the following two cases, and comment on the effect
of the pipe material:
1. Cast iron pipe, ² = 0.26 mm
2. Drawn tubing, ² = 0.0015 mm
Note that I find the pressure drop is 9.1160 kPa for cast iron, and 3.0864 kPa for drawn
tubing. Use these values to debug your code.
Exercise 7.2 Building upon Exercise 7.2, it is common that you measure the pressure
drop via a manometer and use this to calculate the velocity (or in general the flow rate) of
the fluid. I will recast Exercise 7.2 as such a problem.
Air under normal conditions flows through a 4 mm diameter pipe. Over a 0.2 m
section of pipe we measure that the pressure drop, ∆p, is 50 kPa. Knowing this we wish to
determine the average velocity of the flowing air.
For air under normal conditions, the density and viscosity can be taken to be ρ = 1.23
kg/m3 and µ = 1.79 × 10−5 N·s/m2 . The pressure drop (∆p) may be computed as
l 1
∆p = f ρV 2 (7.14)
D2
where l is the length of the pipe, D is the pipe diameter, ρ is the density of the fluid (air),
V is the fluid (air) velocity, and f is the friction factor. For this problem we have turbulant
flow. The turbulant portion of the Moody chart is represented by the Colebrook formula
à !
1 ²/D 2.51
p = −2 log10 + p (7.15)
f 3.7 Re f
where ² is the pipe roughness, and Re is the Reynold’s number, defined as
ρV D
Re = (7.16)
µ
252 Chapter 7 Zero-finding
Compute the average velocity for the following two cases, and comment on the effect
of the pipe material:
1. Cast iron pipe, ² = 0.26 mm
2. Drawn tubing, ² = 0.0015 mm
Note that in reality, when modelling compressible fluids such as air, it is important to
account for the temperature and pressure dependence of ρ and µ. It is ignored here for
simplicity. For the case of the cast iron pipe, I compute a velocity of 141.1115 m/s, and for
drawn tubing I compute a velocity of 281.0068 m/s.
Exercise 7.3 Have you ever heard of a Galilean thermometer1 ? It is a clever thermometer
invented in the 1660s. In fact, you can still find Galilean thermometers available for
purchase today, although today they are mostly used for decoration. The basis of the
thermometer is that the density of a liquid changes with temperature, which in turn
would effect the buoyancy of an object in that liquid. While we will not exactly model a
Galilean thermometer, we will model something similar.
We will model the buoyancy of a spherical object of radius r o and density ρ o . The
volume of a sphere with radius r o is 4/3πr o3 . If a sphere with radius r o is submerged in a
liquid to a depth d , the volume (V ) of the sphere below the liquid line (or equivalently
the volume of displaced liquid) is:
π¡
3r o d 2 − d 3
¢
V=
3
as long as d < 2r ; that is, so long as the object is not submerged! An object floats at the
level where the weight of the displaced liquid equals the total weight of the object. The
weight of the object (Wo ) and the weight of the displaced liquid (Wl ) may be computed
as:
4
Wo = ρ o g Vo = ρ o g πr o3
3
π¡
Wl = ρ l g V = ρ l g 3r o d 2 − d 3
¢
3
where g is the gravitational constant. Equating we have:
4 π¡
ρ o g πr o3 = ρ l g 3r o d 2 − d 3
¢
3 3
And simplifying:
4ρ o r o3 = ρ l 3r o d 2 − d 3
¡ ¢
(7.17)
Okay, now we need some parameters. The spherical object can be assumed to have a
constant density of ρ o = 0.3 g/mL, and have a radius of r o = 100 cm. We will model the
liquid as ethanol, for which the density is described by the following equation2 :
n
ρ l = A · B −(1−T /C ) (7.18)
1 https://en.wikipedia.org/wiki/Galileo_thermometer
2 Yaws, Carl L. (2012; 2013; 2014). Yaws’ Critical Property Data for Chemical Engineers
Exercise 7.4 In the separation of binary mixtures via distillation, the presence of an
azeotrope puts a limit on the achievable separation. For a binary system at vapor/liquid
equilibrium, assuming the vapor phase is an ideal gas and the Poynting correction is
negligible, the relative volatility (α) takes the form
γ1 (T, x 1 ) p 1sat (T )
α (T, x 1 ) =
γ2 (T, x 1 ) p 2sat (T )
where x 1 is the mole fraction of component 1 (x 1 + x 2 = 1), T is the temperature, γ1 and
γ2 are the activity coefficient of component 1 and 2, respectively, and p 1sat and p 2sat are
the pure component vapor pressure of component 1 and 2, respectively. Component 1
corresponds to the most volatile component (MVC) and component 2 is the least volatile
component (LVC). In the design of vapor/liquid separation processes (i.e., distillation),
the greater the deviation of α from unity the easier the separation. When α > 1 the MVC is
concentrated in the vapor phase, when α < 1 the LVC is concentrated in the vapor phase,
and when α = 1 we have an azeotrope, no separation is possible.
Next, we will adopt the standard assumption that γ1 and γ2 are independent of pres-
sure, and will consider only isothermal (constant T ) vapor/liquid equilibrium (P x y). For
this case, α is a function of just a single variable, x 1 . At 313.15 K, the binary system ethyl
acetate(1)/ethanol(2) exhibits an azeotrope. I would like you to solve for the composition
of the azeotrope. Put differently, what value of x 1 results in α = 1? Next, I will provide you
with some additional information to solve the problem.
The vapor pressure of ethyl acetate (p 1sat ) and ethanol (p 2sat ) can be computed using
Antoine’s equation:
B
log10 p sat = A −
T +C
where A, B , and C are constants, p sat is the vapor pressure in units of mmHg, and T is
the temperature in ◦ C. In the table below, I provide parameters along with the minimum
(Tmin ) and maximum (Tmax ) temperature for which use of the parameters is appropriate.
The parameters are all adopted from those in the freely available Dortmund Data Bank.3
3 http://ddbonline.ddbst.com/AntoineCalculation/AntoineCalculationCGI.exe
254 Chapter 7 Zero-finding
Note that the Dortmund Data Bank also provides an online calculator you can use to
make sure your Antoine functions are working correctly.
The activity coefficient can be modeled using the NRTL (non-random two-liquid)
equation:
¶2
G 21 τ12G 12
· µ ¸
ln γ1 = x 22 τ21 +
x 1 + x 2G 21 (x 2 + x 1G 12 )2
¶2
G 12 τ21G 21
· µ ¸
ln γ2 = x 12 τ12 +
x 2 + x 1G 12 (x 1 + x 2G 21 )2
where
g 12 − g 22 g 21 − g 11
τ12 = τ21 =
RT RT
and
y 1 /x 1 y 1 (1 − x 1 )
α= =
y 2 /x 2 y 2 (1 − x 2 )
Using the parameters provided and a molar gas constant of R = 8.314 J/(mol K), I obtain
x 1 = 0.6092. Use this to help you debug your code... if necessary.
4 http://www.ddbst.com/en/EED/VLE/VLE%20Ethanol%3BEthyl%20acetate.php
7.22 Exercises 255
Exercise 7.5 In Example 5.10 we used the ideal gas equation of state and the truncated
virial equation with corresponding states theory to estimate the molar volume of n-butane
at 350 K and 2 bar. Using the truncated virial equation, we computed the compressibility,
Z , and then calculated the molar volume as v = Z v ig , where v ig is the molar volume of
an ideal gas at the same temperature and pressure.
Since our function for the truncated virial (v_virial) is dependent on our function
for the molar volume of an ideal gas (v_ideal_gas), in Section 6.1 we decided to combine
the functions into a single file, where v_virial is the top function and v_ideal_gas is
the helper function. Then in Exercise 6.4 we vectorized our code. Let’s keep the fun going
here!
So far we have used the truncated virial equation in its natural form, an expression
explicit in molar volume (or equivalently the compressibility, Z ). Knowing the pure
component constants, Tc , p c , and ω, and specifying the temperature and pressure of
interest, the molar volume is readily computed.
But what if the pressure and molar volume were specified, and you were asked to
solve for the temperature? While I would not attempt to solve this problem analytically,
now using fzero this is entirely possible. Let’s give it a try!
Recall that from the NIST WebBook5 for n-butane we have: Tc = 425.125 K, p c =
37.960 bar, and ω = 0.201. For a pressure of p = 2 bar and molar volume of v = 1 × 104
cm3 /mol, what is the temperature? You must use fzero in your solution. To check the
reasonableness of your solution, have a look at Figure 6.3 in the solution manual.
5 http://webbook.nist.gov/chemistry/fluid/
Chapter 8
Systems of Equations
In Chapter 7 we learned how to use MATLAB for zero-finding and to find the
roots of polynomials. In Chapter 8 we continue and solve systems of linear and
non-linear equations. By the end of this chapter you will be able to:
• Exhibit ability to construct vectorized “error” functions for use with fsolve
8.1 Matrices
A matrix is a two-dimensional version of a vector. Like a vector, it contains
elements that are identified by indices. The difference is that the elements are
arranged in rows and columns, so it takes two indices to identify an element.
We have already seen a number of ways to create a matrix of a given size (see
Section 4.13 on page 97); okay, we created vectors, but a vector is a matrix. To
create a matrix in which all entries are 1, we can use the built-in ones function.
We have used this function so far as ones(1,N) which creates a vector of length
N. The first entry (1) actually corresponds to the number of rows and the second
entry corresponds to the number of columns (N). What we have been calling a
vector in MATLAB you can equally call a “row” vector; it is a single rowed matrix.
You could also create a “column” vector as ones(N,1). Another common usage is
ones(N) creates a square matrix (i.e., same number of columns and rows) with
N columns and rows. We have also used the built-in function zeros. It behaves
just like ones, only every entry is assigned a value of 0. The built-in function
rand takes the same form too. With rand, each element is a uniformly distributed
pseudo-random number between 0 and 1; put simply, you can think of it as
random number between 0 and 1.
257
258 Chapter 8 Systems of Equations
Sometimes you may find you wish to create a matrix of ones or zeros which is
the same size as a matrix which is already defined in your workspace. You can do
this quite easily. Say matrix A is defined in your workspace. Then size(A) returns
the size (or dimensions) of matrix A. You then need just pass this result to ones or
zeros. Or combine the statements as ones(size(A)).
Another of many ways to create a matrix is the magic function, which returns
a “magic” square matrix (n by n) with the given size:
>> M = magic(3)
M = 8 1 6
3 5 7
4 9 2
If you don’t know the size of a matrix, you can use whos to display it:
>> whos
Name Size Bytes Class Attributes
M 3x3 72 double
>> V = size(M)
V = 3 3
The first element is the number of rows, the second is the number of columns.
To read an element of a matrix, you specify the row and column number(s):
>> M(1,2)
ans = 1
>> M(2,1)
ans = 3
When you are working with matrices, it takes some effort to remember which
index comes first, row or column. I find it useful to repeat “row, column” to myself,
like a mantra. You might also find it helpful to remember “down, across,” or the
abbreviation RC.
Another way to create a matrix is to enclose the elements in brackets, with
semi-colons between rows:
>> size(D)
ans = 2 3
As you begin to work with row and column vectors, and matrices, remem-
ber our discussion of incremental development (Section 2.8) and unit testing
(Section 2.9) from Chapter 2. Practice creating matrices and referring to specific
elements in the Command Window to test your understanding. To be honest, this
is still something I often do when writing code; sometimes there is too much to
remember, so leverage the interactive abilities of MATLAB.
Something we will find very useful in the future is colon notation. It will
be given much more attention later, but I will introduce it here while we are
discussing matrices. Many times we will want to isolate all of the elements of
a specific row or column in a matrix. If I look at D like a table, perhaps row 1
contains data for a specific time at which a measurement was made. We can
isolate this row using colon notation:
>> D1 = D(1,:)
D1 = 1 2 3
We can interpret (1,:) as row 1, all columns. You could likewise use it to isolate
a specific column. To isolate just the second column, we will use (:,2) meaning
all rows, second column:
>> D2 = D(:,2)
D2 = 2
5
which is a column vector, which we will discuss momentarily. You may also re-
member from our discussion of for loops that colon notation can be used to
create a vector, and we then later saw how vectors of indices (think of the find
command) can be used to isolate elements of a vector. Let’s put it together:
>> Dr = D(:,2:3)
Dr = 2 3
5 6
Is the result what you expected? While we could spend much more time on this,
let me make just one more point. We see that the use of comma notation is
very useful for isolating specific parts of a matrix. This can be useful to perform
operations (+, –, .*, ./, etc.), logical comparisons, and to apply functions
such as find only to a specific part or range of a matrix. This also gives us a way to
use length, which only applies to vectors, to find the number of rows or columns.
(But size is just as easy.)
260 Chapter 8 Systems of Equations
>> x = 5;
>> size(x)
ans = 1 1
>> R = 1:5;
>> size(R)
ans = 1 5
Well, some vectors, anyway. Actually, as we have already mentioned, there are
two kinds of vectors. The ones we have seen so far are called row vectors, because
the elements are arranged in a row; the other kind are column vectors, where the
elements are in a single column.
One way to create a column vector is to create a matrix with only one element
per row:
>> C = [1;2;3]
C =
1
2
3
>> size(C)
ans = 3 1
>> length(C)
ans = 3
The difference between row and column vectors is important in linear algebra,
but for most basic vector operations, it doesn’t matter. When you index the ele-
ments of a vector, you don’t have to know what kind it is:
>> R(2)
ans = 2
8.3 The transpose operator 261
>> C(2)
ans = 2
>> Dt = D'
Dt = 1 4
2 5
3 6
What effect does the transpose operator have on row vectors, column vectors,
and scalars? Let’s start with row vectors. We will define a row vector and then
compute the transpose.
>> X = 1:5
X = 1 2 3 4 5
>> Xt = X'
Xt = 1
2
3
4
5
>> size(X)
ans = 1 5
>> size(Xt)
ans = 5 1
262 Chapter 8 Systems of Equations
>> Xt'
ans = 1 2 3 4 5
y = −x − 3
x 2 + y 2 = 17
8.5 System of equations 263
At first glance you might think you could solve these equations by calling fzero
once to solve for x and once to solve for y. The problem is that each equation
involves both variables, which is what makes this a system of equations and
not just a list of unrelated equations. To solve a system, you have to solve the
equations simultaneously. Fortunately, MATLAB has a built-in function fsolve
that can handle systems of equations.
Working with fsolve is analogous to fzero. Let us therefore start by finding t Also, while not discussed
the zeros of a single equation. For this case, the use of fsolve will look exactly here, fsolve can be ap-
plied to non-square systems
like fzero. Imagine we would like to solve the equation x 2 − 2x = 3. We begin by
of equations; that is, where
writing an error function, such that the function evaluates to zero at a solution: the number of equations
x 2 − 2x − 3 = 0. is greater than the number
of unknowns, or when the
Listing 8.1 error_func.m number of unknowns is
greater than the number of
1 function res = error_func(X) equations. For these cases,
2 res = X.^(2)-2.*X-3;
fsolve acts as a minimizer.
3 end
I hope to add a chapter on
the topic in the future.
Let’s assume we are searching for the root over the range 0 < x < 5. Solving using
fzero: t Note that fsolve is part of
>> fzero(@error_func,[0,5]) MATLAB’s Optimization
ans = 3 Toolbox. If you obtain an
error message indicating
With fzero we could alternatively provide an initial estimate of the zero location that the command fsolve
is unknown, you likely did
instead of a range to search.
not include the Optimiza-
>> fzero(@error_func,4) tion Toolbox in your initial
ans = 3.0000 MATLAB installation.
Solving using fsolve, the same error function is needed. The only difference
for this case is you can only provide an initial estimate of the zero location (and
not a range).
>> fsolve(@error_func,4)
ans = 3.0000
A perfect match! Note that with this call you would also receive the following
information:
Equation solved.
What is most important is the top line, “Equation solved”. A discussion of the
264 Chapter 8 Systems of Equations
theory behind fsolve is beyond the scope of this class, so we will not worry
about the rest of the message here. To learn more about fsolve, have a look at
the MATLAB documentation. In Example 8.2 we will look at how the options used
by fsolve can be updated, including changing the tolerance and suppressing the
additional information displayed with the solution (i.e., “Equation solved”).
The major reason for using fsolve over fzero is that it can be used to solve
systems of equations. Using fsolve to solve a system of equations is very similar
to solving a single equation. We will need to create an error function. Just as with
fzero and the single equation case with fsolve, the error function will have just
a single input and a single output variable. The input variable will be a vector
containing the current estimate/value of the variable(s) you are solving for, and
the output will be a vector containing the value of the error function for each
equation in the system.
Let’s consider the system of equations we started our discussion with. We first
re-write our equations as a system of error functions.
y +x +3 = 0
x 2 + y 2 − 17 = 0
Next, write our error function M-file. Just as before, fsolve expects that our
function has a single input variable. Here the input variable will be a vector of
length 2. The first element will be x and the second element will be y. Note that
you can list the variables in whatever order you would like, you just need to make
sure you are consistent throughout the problem.
The input variable is a vector with two elements, the first element is x and the
second element is y. I gave it a capital letter to remind me that it is a vector. The
body of the function includes three paragraphs, each explained by a comment.
The first paragraph unpacks the vector by copying the elements into scalar
variables. This isn’t necessary, but giving names to these values helps me remem-
ber what’s what. It also makes the second paragraph, where we compute the
error functions, resemble the mathematical equations we were given, which helps
prevent errors.
8.6 What can go wrong? 265
The last paragraph packs the computed error functions back into a vector.
When fsolve calls this function, it provides a vector as input and expects to get a
vector as output.
Calling fsolve from the Command Window will look identical as before. Only
since we now have a system of equations, we need to provide an initial estimate
of each variable. This problem can be solved analytically to find that x = −4 and
y = 1 is a solution, as is x = 1 and y = −4. Solving with fsolve:
>> x0 = 0;
>> y0 = -5;
>> X0 = [x0,y0]
>> Sol = fsolve(@error_function_series,X0)
Sol = 1.0000 -4.0000
Nice! The solution is a vector of length 2. The first element corresponds to x, and
the second element corresponds to y. The order of this vector agrees exactly with
the order of your vector with the initial guesses and the input vector to your error
function M-file.
>> x = Sol(1);
>> y = Sol(2);
Just as with fzero, we find that fsolve returns a single zero location. To find
the other we need to start with a different set of initial guesses.
>> x0 = -5;
>> y0 = 0;
>> X0 = [x0,y0]
>> Sol = fsolve(@error_function_series,X0)
Sol = -4.0000 1.0000
>> x0 = 2;
>> y0 = 2;
>> X0 = [x0,y0];
>> Sol = fsolve(@error_function_series,X0)
Sol = 2.7803 2.7803
No solution found.
So MATLAB returns a solution, but it is not correct. You can confirm this by
passing the result to the error function:
>> error_function_series(Sol)
ans = 8.5607 -1.5395
In this particular case, all we need to do is update the initial guess so that x 6= y.
>> x0 = 1;
>> y0 = 2;
>> X0 = [x0,y0]
>> Sol = fsolve(@error_function_series,X0)
Sol = -4.0000 1.0000
So we find that the success of fsolve is sensitive to the initial estimate of the
location of the zeroes. In general, the better the estimate, the better off you will
be. If the system of equations corresponds to a physical problem, we can typically
provide a reasonable initial estimate. For example, if I were solving for mole
fraction compositions, I know they will be between 0 and 1. So 0.5 might be a
good initial guess. This isn’t always the case, and we will see in the Examples
how rand can be used. This also brings us back to the point of using fzero over
fsolve. If you have a single equation with a single unknown that changes sign
over the interval of interest, use fzero; it will be guaranteed to work.
Additionally, this leads to the question if we were using fsolve as part of a
larger script or function, how can we check that it worked without looking at the
resulting message? In Section 6.4 we saw how we could write our functions to
return multiple/optional output variables. This is how most of MATLAB’s built-in
functions work. The first output variable provided by fsolve is the estimate of
the solution to the system of equations. The second (optional) variable is the
value of the system of error functions using the estimated solutions. And the
third (optional) variable is the exit flag. A value of 1, 2, 3 or 4 all mean “Equation
solved;” it worked! A value of 0 means it did not work because the number of
iterations were exceeded. Increase the number of iterations and maybe you will
be successful. And a negative value means... bad news, it didn’t work. Let’s revisit
our last two calls with our new tricks.
8.6 What can go wrong? 267
>> x0 = 2;
>> y0 = 2;
>> X0 = [x0,y0]
>> [Sol,fun_val,flag] = fsolve(@error_function_series,X0)
Sol = 2.7803 2.7803
fun_val = 8.5607 -1.5395
flag = -2
The value of fun_val agrees with what we computed previously, and since the
exit flag is not –2, the result is not reliable.
>> x0 = 1;
>> y0 = 2;
>> X0 = [x0,y0]
>> [Sol,fun_val,flag] = fsolve(@error_function_series,X0)
Sol = -4.0000 1.0000
fun_val =
1.0e-12 *
0.0018 0.3766
flag = 1
Figure 8.1 Snippet of the documentation page for fsolve that describes
the first three output variables.
268 Chapter 8 Systems of Equations
8.7 Examples
Example 8.1
Solve the following system of equations:
1
y = x −5
2
y = x 2 + 2x − 15
Solution: (Link to screen cast and accompanying M-file.) What fun, a system
of non-linear equations. Let’s begin by solving by hand. Let’s subtract the first
equation from the second. This kills y and results in:
3
x 2 + x − 10 = 0
2
p
−b ± b 2 − 4ac
x=
2a
>> X = quadratic_eq(1,1.5,-10)
X = 2.5000 -4.0000
>> Y = 0.5*X - 5
Y = -3.7500 -7.0000
In the interest of more practice, let’s go ahead and use roots and confirm we get
the same answer:
8.7 Examples 269
>> X = roots(C)
X = -4.0000
2.5000
>> Y = 0.5*X - 5
Y = -7.0000
-3.7500
Now let’s solve numerically using fsolve. We begin by writing our equations so
that they evaluate to zero at the solution:
1
y − x +5 = 0
2
y − x 2 − 2x + 15 = 0
Next we can solve in the Command Window . Remember, we will need to provide
an initial estimate of the solution. Rather than guess, let’s see what happens if we
use the random number generator to generate them for us. I will generate initial
guesses over the range –5 to 5.
>> fsolve(@error_ex1,10*rand(1,2)-5)
ans = -3.7500 2.5000
>> fsolve(@error_ex1,10*rand(1,2)-5)
ans = -3.7500 2.5000
>> fsolve(@error_ex1,10*rand(1,2)-5)
ans = -3.7500 2.5000
270 Chapter 8 Systems of Equations
>> fsolve(@error_ex1,10*rand(1,2)-5)
ans = -7.0000 -4.0000
There we have it! We were able to get both sets of solutions. Remember, the way
I set-up my error function, the first value is y followed be x. The results agree
perfectly with what we found by hand.
Example 8.2
Solve the following system of equations:
xy = 1
x+y =2
Solution: (Link to screen cast and accompanying M-file.) Here there is only one
unique solution.
By inspection, we know the solution will be x = y = 1. Let’s see how fsolve does.
Let’s re-write our equations so that they equal zero, then create our error function.
xy −1 = 0
x + y −2 = 0
Just as in the last problem, I will solve by taking an initial guess of my solutions
using the random number generator with values between –5 and 5.
>> fsolve(@error_ex2,10*rand(1,2)-5)
ans = 0.9994 1.0006
8.7 Examples 271
The numeric answer is not exactly x = y = 1. We can confirm by passing the result
to our error function.
>> error_ex2(ans)
ans =
1.0e-06 *
-0.3326 -0.0000
It’s not quite zero, but it is very close, and likely close enough for most applications.
In the interest of having fun unit testing, if you wished to improve the accuracy of
the calculation, this is a matter of the tolerance used by fsolve to determine if it
is close enough. You can have a look at MATLAB’s documentation, but the default
tolerances are 1 × 10−6 . Let’s make them smaller and see what happens. To change
the options, have a look at the documentation page on “optimoptions” and the
options table in the documentation page for fsolve. Since we will have an interest
in changing the tolerance, also see the documentation page on “Tolerances and
Stopping Criteria.”
Let’s begin by displaying the current, default options:
options =
fsolve options:
Set properties:
No options set.
Default properties:
Algorithm: 'trust-region-dogleg'
CheckGradients: 0
Display: 'final'
FiniteDifferenceStepSize: 'sqrt(eps)'
FiniteDifferenceType: 'forward'
FunctionTolerance: 1.0000e-06
MaxFunctionEvaluations: '100*numberOfVariables'
MaxIterations: 400
OptimalityTolerance: 1.0000e-06
OutputFcn: []
PlotFcn: []
SpecifyObjectiveGradient: 0
StepTolerance: 1.0000e-06
TypicalX: 'ones(numberOfVariables,1)'
UseParallel: 0
You can match up all of the options under Default properties in the options
272 Chapter 8 Systems of Equations
options2 =
fsolve options:
Set properties:
FunctionTolerance: 1.0000e-12
OptimalityTolerance: 1.0000e-12
Default properties:
Algorithm: 'trust-region-dogleg'
CheckGradients: 0
Display: 'final'
FiniteDifferenceStepSize: 'sqrt(eps)'
FiniteDifferenceType: 'forward'
MaxFunctionEvaluations: '100*numberOfVariables'
MaxIterations: 400
OutputFcn: []
PlotFcn: []
SpecifyObjectiveGradient: 0
StepTolerance: 1.0000e-06
TypicalX: 'ones(numberOfVariables,1)'
UseParallel: 0
Now when we call fsolve we can provide an additional input argument corre-
sponding to our set of options. I will use both the initial, default options we stored
to options, and then our updated set in options2.
Nice! What if we wanted to update options2 to also not display the message with
the solution? The relevant option is Display, and we can change its value as:
8.8 One final fsolve note 273
options2 =
fsolve options:
Set properties:
Display: 'none'
FunctionTolerance: 1.0000e-12
OptimalityTolerance: 1.0000e-12
Default properties:
Algorithm: 'trust-region-dogleg'
CheckGradients: 0
FiniteDifferenceStepSize: 'sqrt(eps)'
FiniteDifferenceType: 'forward'
MaxFunctionEvaluations: '100*numberOfVariables'
MaxIterations: 400
OutputFcn: []
PlotFcn: []
SpecifyObjectiveGradient: 0
StepTolerance: 1.0000e-06
TypicalX: 'ones(numberOfVariables,1)'
UseParallel: 0
And last, if you wanted to reset options2 to contain the default values, we need
just go back to our starting command: options2 = optimoptions(’fsolve’).
If the options used here do not work for you, have a look at the documentation page
“Current and Legacy Option Name Tables.” Many of the option names changed in
MATLAB R2016a. If you are using GNU Octave, consult its documentation.
2x + 3y = 2 (8.1)
x + 4z = 1 (8.2)
y +z =4 (8.3)
Let’s solve! First, we start by noticing that the system of equations is square. We
have 3 equations and 3 unknowns. So it can be solved. I might start by adding
−4×eq. 8.3 to eq. 8.2. This would result in:
x − 4y = −15 (8.4)
Next, add 43 eq. 8.4 to eq. 8.1. This results in:
equations as unknowns. This is not a mathematics class, so I will leave this at that.
8.10 Gaussian elimination 275
We can then solve this equation and get x = −3.3636. Now that we have x, we can
go to eq. 8.4 and eliminate x to solve for y = 2.9091. And then finally we could
go back to eq. 8.3 to eliminate y and find that z = 1.0909. Nice! We just solved a
system of of linear equations using an elimination strategy.
We could also represent the system of equations as a matrix and solve in
matrix form. We create a matrix by first re-writing the equations so that every
variable is present in each equation, and I will order the variables from left to
right as x, y, z.
2x + 3y + 0z = 2 (8.6)
x + 0y + 4z = 1 (8.7)
0x + y + z = 4 (8.8)
Where a variable was previously missing, I add zero times the variable. (Remem-
ber anything times zero is zero.) Next, we will construct a matrix containing the
coefficients and the right-hand-side of the equality. Row 1 will be eq. 8.6, row
2 will be eq. 8.7, and row 3 will be eq. 8.8. Column 1 will be the coefficient of x,
column 2 will be the coefficient of y, column 3 will be the coefficient of z, and
column 4 will be the right-hand-side of the equality.
2 3 0 2
1 0 4 1
0 1 1 4
Just as we previously multiplied equations by a constant and then added equa-
tions together, we would do the same thing here with our rows. The goal would be
for elements (1,1), (2,2), and (3,3) to be 1, while the other elements in rows 1–3 and
column 1–3 are zero. If we were then to translate back to equation form, we would
find that we just solved for x, y, and z. Solving in matrix form we would perform
row reductions, where the three elementary row operations are: 1) replacement,
2) interchange, and 3) scaling. The final form of the matrix just described would
correspond to reduced row echelon form. Algorithms exist to obtain the reduced
row echelon form of a matrix. MATLAB has a built-in function to do just this
called rref. Here is how we would use it to solve this system of equations:
1.0000 0 0 -3.3636
0 1.0000 0 2.9091
0 0 1.0000 1.0909
276 Chapter 8 Systems of Equations
A perfect match! If you wished to filter out the roots, you could do so in a number
of ways. We could create a vector:
>> S = Sol(:,4)
S =
-3.3636
2.9091
1.0909
This is a column vector. If you preferred a row vector, you could take the transpose.
This would give a result analogous if we had used fsolve. Know that you could
use fsolve to solve a system of linear equation. Use of rref is preferred, as
it avoids the need of initial conditions and you can be confident that it will be
successful.
>> r = rank(A)
r = 3
>> c = length(A(1,:))-1
c = 3
>> c = length(A(1,1:end-1))
c = 3
Here, A(1,:) corresponds to the first row of matrix A, all columns. So it pulls off
the first row as a row vector. We want the length of this vector minus 1; minus 1
because the last column corresponds to the right-hand-side of the equality in the
original equations. Equivalently, A(1,1:end-1) corresponds to the first row of
matrix A, and columns 1 to the second last (i.e., the last column minus 1). You can
8.12 Left division 277
check to make sure that the number of variables is equivalent to the rank. If not,
the system of equations is not square.
So what would be an example of a non-square system of equations? First, you t This corresponds to having
could have more equations than unknowns or more unknowns than equations. more unknowns than equa-
tions. Maybe a better way
The more common scenario is the system of equations appears square, but two or
to phrase is would be more
more of the equations are linear combinations of each other. What if we slightly unknown than linearly inde-
changed our system of equations to the following: pendent equations.
2x + 3y = 2 (8.9)
2x + 4y + z = 6 (8.10)
y +z =4 (8.11)
Upon inspection, we find that the system of equations is not square. Eq. 8.10 is
equal to the sum of eqs. 8.9 and 8.11. Let’s see what happens if we attempt rref
and rank.
>> rb = rank(B)
rb = 2
Does the output make sense to you? A system of linear equations will have a
single, unique solution if the system of equations is square. This is why you spent
so much time learning how to set-up problems properly in your Mass and Energy
Balance course. As always, have a look at the documentation pages for rref and
rank to learn more.
2 3 0 x 2
1 0 4 y = 1
0 1 1 z 4
We find that we get exactly the same solution as with rref using either notation,
where inv is the MATLAB function to compute the inverse of matrix, where
inv(A) and Aˆ(-1) are equivalent. You can use either solution strategy (and
either notation) and obtain the same result.
Know that in general I try to avoid left division. Why? Because too often I
confuse myself and make a mistake. Left division is not an operation I am used
to, and I suspect most of you have never heard of it before. What we commonly
refer to simply as division, could just as well be called right division and is desig-
nated with a forwardslash (/). Left division is designated with a backslash (\). To
understand the difference, let’s perform some unit testing in MATLAB.
>> 8/4
ans = 2
>> 8\4
ans = 0.5
8.13 Application: Raoult’s Law 279
We find for this simple scalar case that left division essentially reverses the division.
Again, this tends to confuse me, so I stick to using inv.
3. We are well removed from the critical point such that f i0 ≈ p isat
If you do not know what this means, no worries, just be sure to sign-up for
Chemical Engineering Thermodynamics before you graduate. A situation where
Raoult’s law would be appropriate would be to model mixtures of alkanes at
ambient pressures.
A binary system at vapor/liquid equilibrium is governed by the following
system of non-linear equations:
y 1 p = x 1 p 1sat (8.12)
y 2 p = x 2 p 2sat (8.13)
where y 1 and y 2 are the vapor phase mole fractions of component 1 and 2, re-
spectively, x 1 and x 2 are the liquid phase mole fractions of component 1 and 2,
respectively, where y 1 + y 2 = 1 and x 1 + x 2 = 1. The pressure is given by p, and p 1sat
and p 2sat are the pure component vapor pressure of component 1 and 2, respec-
tively. The pure component vapor pressure is only a function of temperature, and
is commonly computed using Antoine’s equation:
B
log10 p sat = A − (8.14)
T +C
If we add eqs. 8.12 and 8.13 together we obtain:
available for free from the Dortmund Data Bank (DDBST). We find the following
Antoine parameters, where the pressure is in mmHg and the temperature is in ◦ C
2
:
Let’s test our code for the binary system hexane(1)/cyclohexane(2) at 70 ◦ C (or
343.15 K) and x 1 = 0.125. At these conditions, experimental values from DDBST
are y 1 = 0.179 and p = 77.327 kPa. 3 To facilitate the calculation, I will create
another function specific to this system.
3 http://www.ddbst.com/en/EED/VLE/VLE%20Cyclohexane%3BHexane.php
8.13 Application: Raoult’s Law 283
This is in excellent agreement with the reference data. Nice! Note that I could
have made raoult_tx a helper function within hexane_cyclohexane_tx, but I
did not do so here in the interest of space.
Alternatively, we could have solved the problem analytically. Remember that
p 1sat and p 2sat are only a function of T . So by specifying T , p 1sat and p 2sat are fixed
and can be computed directly using Antoine’s equation (eq. 8.15). Therefore,
we could first solve directly for p via eq. 8.15, and then solve for y 1 directly as:
y 1 = x 1 p 1sat /p.
284 Chapter 8 Systems of Equations
Again, with both methods we get the same answer. So which solution is preferred?
Again, this is a difficult question to answer. It is nice to solve sequentially and use
fzero. We know that so long as our error function is continuous and changes
signs, then it is guaranteed to find a solution. Both of these criteria will be satisfied
for this physical problem. But again, if working up the problem the allow you to
solve sequentially creates the opportunity for mistakes, then use of fsolve might
be preferred. I offer you both solution strategies to allow you to compare.
292 Chapter 8 Systems of Equations
place.
Let’s write a mole balance for our system for component A:
t This is a homogeneous, Consider an arbitrary species A involved in a chemical reaction. We will as-
bulk phase reaction. The sume that the only way a mole of A can be generated/consumed is via a chemical
rate of reaction is therefore
reaction. If we consider here just a single chemical reaction taking place:
made intensive by dividing
by the volume of the system.
GENERATION = ν A V r
If we double the size of the
system we expect that rate where ν A is the stoichiometic coefficient of A, ν A r is the per unit volume rate of
of generation/consump-
generation/consumption of A, and V is the volume of the system. With this our
tion to double, so volume
is the natural scale factor.
mole balance design equation becomes:
However, there are other
INPUT + ν A V r = OUTPUT
conventions. A heteroge-
neous, catalytic reaction
Last, we designate our inlet molar flow rate of component A as ṅ 0A and the outlet
takes place at the surface
of a solid catalyst. (It is het- molar flow rate of component A as ṅ A . This leads to the final expression:
erogeneous because the
ṅ 0A + ν A V r = ṅ A
catalyst is solid and in the
presence of either a liquid
Or more commonly:
or gas.) For this case, the
rate of reaction is made in- ν A V r = ṅ A − ṅ 0A
tensive by dividing by the A common measure of the reaction progress is the fractional conversion. The
surface area of the catalyst.
fractional conversion of A may be computed as
This is again the natural
scale factor because if we ṅ 0A − ṅ A
double the surface area fA =
of the catalyst, we expect ṅ 0A
the rate of generation/con-
It tells us what fraction of A in the inlet has been consumed.
sumption to double.
k = k 0 exp[−E / (RT )]
k 0 = 2.4 × 108 s−1
E = 15.3kcal/mol
Okay, let’s start with some simplifying assumptions. We have a reaction that t Also note here that having
takes place in the aqueous phase. The initial concentration of A is rather dilute, a dilute solution is a main
reason we can assume the
2 moles per liter of water. There are approximately 55.35 moles of water per
system is isothermal. There
liter, so this corresponds to a mole fraction of just 0.035. And since it is a 1 to 1
is an excess amount of wa-
reaction (for every mole of B created a mole of A is consumed), the total molar ter present that serves as a
concentration of A and B will remain the same (a small value). Therefore, it is heat bath
very reasonable to assume that the inlet and outlet volumetric flow rate (V̇ ) are
constant and equal to 1200 L/hour. This is a very common approximation.
This allows us to do a number of things. First, we can write our inlet and outlet
molar flow rates in terms of molar concentrations and volumetric flow rates:
ṅ 0A = C A0 V̇
ṅ A = C A V̇
Additionally, we can re-write the fractional conversion using molar concentra-
tions:
C 0 −C A
fA = A 0
CA
In the present problem I asked you to size the reactor. That is, for a given flow
rate, find the total reactor volume needed. I just as well could have specified the
total volume and asked you to find the inlet flow volume. The problem can be
generalized by defining the space time:
V
τ=
V̇
The space time has units of time, and can be thought of as the average time a
fluid element remains inside the reactor. Solving with respect to space time is
also useful because it will facilitate comparison to a batch reactor or plug flow
reactor (PFR).
Let us also pause and take note of the units. I have provided you with the
general rate expression. If you want a rate expression for the consumption of A
or generation of B, you multiply by the appropriate stoichiometric coefficient.
Remember stoichiometric coefficients for reactants are negative (they are being
consumed) and positive for reactants. So for the rate of consumption of A
r A = ν A r = −kC A
r B = νB r = kC A
296 Chapter 8 Systems of Equations
Get the idea? The units of the rate will be moles per volume per time. We will need
to make sure we are using the same units for moles, volume, and time throughout
the problem. The rate constant (k) has the same units as the pre-exponential
term (k 0 ) of 1/s. However, the flow rate given in the problem is in L/h.
We will need to make sure our units are consistent when you solve. The term
in the exponential, E /(RT ) is dimensionless. We are given E in kcal/mol. So
unless we convert it, we should use R in units of kcal/(mol K) and T in units of K.
We could alternatively convert E to say J/mol and use the only value of R that I
remember, R = 8.314 J/(mol K).
Additionally, in this problem we have set everything up to solve using A. As a
future note, know that you do not have to use A if you do not want to. Likewise,
we do not need to solve a mole balance for B in order to determine the molar flow
rate of B. The moles of A and B are related by stoichiometry. For every mole of A
that is consumed, a mole of B is created in our reaction. Therefore,
ṅ 0A − ṅ A = ṅ B − ṅ B0
ṅ 0A − ṅ A = ṅ B
C A0 −C A = C B
My very last note is on the rate expression itself. Here we have a relatively
simple expression. This is generally not the case. Depending on the proposed
mechanism or if we have a reversible reaction, the expression will likely be much
more complicated. (No worries, MATLAB does not care!) But the point I would
like to make is that you can’t (and never should be expected to) just look at a
reaction and know what the form of the rate of reaction should be. The only way
is to propose a model (in various ways) and test it by fitting to experimentally
generated data.
Example 8.3
The conversion of A to B (A → B ) takes place in an aqueous solution in an isother-
mal CSTR. The inlet to the reactor is a 2 M solution of A at 300 K at a total flow
rate of 1200 L/hour. (Recall 1 M = 1 mol/L.) Calculate the volume of the reactor
necessary to reach 0.8 fractional conversion of A. The reaction is first order in A and
the rate coefficient obeys the Arrhenius expression with a pre-exponential term
equal to 2.4 × 108 s−1 and an activation energy of 15.3 kcal/mol. Mathematically
this means
r = kC A
k = k 0 exp[−E / (RT )]
k 0 = 2.4 × 108 s−1
E = 15.3kcal/mol
8.14 Application: CSTR 297
ν A V r = ṅ A − ṅ 0A
ṅ A = V̇ C A
ṅ 0A = V̇ C A0
where V̇ is the volumetric flow rate which can be assumed constant. Plugging into
our mole balance design equation we have:
ν A V r = V̇ C A −C A0
¡ ¢
(8.16)
r = kC A
where we are provided with the parameters to compute k. In the problem state-
ment we are additionally provided with values of C A0 and V̇ . Plugging the rate
expression into eq. (8.16), we find we have a single equation with two unknowns:
C A and V . To solve, we need another equation. We are provided with the constraint
that the fractional conversion is 0.8:
ṅ 0A − ṅ A C A0 −C A
fA = = (8.17)
ṅ 0A C A0
Equation (8.16) and 8.17 corresponds to a system of two equation with two un-
knowns which can be solved. Note that eq. (8.17) could be solved first to find C A ,
and then used in eq. (8.16) to computed V . This would involve an fzero call for
each case or you could solve analytically. Alternatively, we can just use fsolve to
solve the system of equations. Both work and will yield the same result. I will use
the latter approach.
Before solving, a few comments on about units. The unit of volume used by V ,
V̇ , C A and C A0 will be liter (L). The parameters provided in the problem statement
use a unit of time of seconds for k and hours for V̇ . A unit conversion will be
necessary to make sure both use the same unit of time. Last, the term E / (RT ) is
dimensionless and T must be in absolute units. I will convert E to J/mol and use
Kelvin for T . Let’s do it!
298 Chapter 8 Systems of Equations
54 r = k*cA;
55
56 % Now computing the error functions.
57 % The mole balance re-written as an error function
58 res1 = nuA*v*r-v_Ls*(cA-cA0);
59 res2 = fA - (cA0-cA)/cA0;
60
61 % Packing up the rate functions
62 res = [res1,res2];
63
64 end
We find we need a volume of 778.3349 L. Note that I tried other initial estimates of
V , but the final value seemed relatively insensitive to the initial guess.
Note, I created a function with no inputs to solve the problem. You will find that
I do this a lot. You need just run the function to obtain the solution. However,
note that beginning with MATLAB R2016b, you can now add functions to scripts to
achieve the same effect.
Example 8.4
Next, generalize your solution to the previous example. Assume that the inlet
volumetric flow rate was not specified. (But do assume it is large enough to assume
the volumetric flow rate is constant.) Solve for and create a plot of the fractional
conversion of A as a function of the space time. Solve for the space time required
to achieve a fractional conversion of 0.8. In general, as space time increases
does fractional conversion increase or decrease? So if one wished to increase the
fractional conversion of a CSTR, what should they do to the inlet volumetric flow
rate (or the reactor volume if possible)?
Solution: Next, we generalize eq. (8.16) to use space time, where τ = V /V̇ . This
gives:
ν A τr = C A −C A0 (8.18)
We have two tasks. First, knowing ν A , k, and C A0 , solve for the required τ to achieve
a fractional conversion of 0.8. The solution is virtually the same as the previous
example. Second, loop over values of τ, and for each value of τ solve for and plot
f A . To accomplish this, with a specified value of τ, we can solve eq. (8.18) for C A ,
which can then be used to compute the fractional conversion. For this case, we will
have a single equation with a single unknown, so the use of fzero will be preferred
over fsolve. Note that we could alternatively solve the expression analytically
for C A for this simple case. But it is easy enough to solve numerically, and the
solution would be the same no matter how complicated the rate expression ever
300 Chapter 8 Systems of Equations
became. Following our previous note on units, τ and k will use the same units of
time. I will use seconds to solve. However, the resulting time to achieve a fractional
conversion of 0.8 is very large in seconds, so I will convert the output to minutes to
make the output more readable.
48 CA(i) = fzero(cstr_error_func,[0,cA0]);
49 end
50 % Computing the fractional conversion
51 FA = (cA0-CA)./cA0;
52
53 % Plotting
54 hold on
55 plot(Tau,FA,'-k')
56
57 % Just for fun, plot our point corresponding to a fractional conversion
58 % of 0.8.
59 plot(res(2),0.8,'ko')
60
61 xlabel('\tau [min]')
62 ylabel('f_{A}')
63 print('-depsc','cstr_single_tau.eps')
64
65 end
66
67 % Error function corresponding to our mole balance design equation
68 % and fractional conversion. The first input variable is a vector of length
69 % 2 containing cA and tau which we will solve for using fsolve. The
70 % remaining variables are parameters that will be passed via an anonymous
71 % function.
72 %
73 function res = cstr_mole_balance_08(CT,cA0,nuA,k)
74 % Un-packing
75 cA = CT(1);
76 tau = CT(2);
77
78 % The desired fractional conversion
79 fA = 0.8;
80
81 % The rate expressions
82 r = k*cA;
83
84 % Now computing the error functions.
85 % The mole balance re-written as an error function
86 res1 = nuA*tau*r-(cA-cA0);
87 res2 = fA - (cA0-cA)/cA0;
88
89 % Packing up the rate functions
90 res = [res1,res2];
91 end
92
93 % Error function corresponding to our mole balance design equation.
94 % In this version, tau will be specified and we will just solve for
95 % cA. The first input variable is cA which we will solve for using fzero,
96 % and the remaining variables are parameters that will be passed via an
97 % anonymous function.
98 %
99 function res = cstr_mole_balance(cA,tau,k,nuA,cA0)
100 % Converting the space time from minutes to seconds
101 t = tau*60;
102
302 Chapter 8 Systems of Equations
0.9
0.8
0.7
0.6
fA
0.5
0.4
0.3
0.2
0.1
0
0 50 100 150 200 250
[min]
We find that as space time increases, fractional conversion increases. The rate of
increase is greatest initially and then slows. If one wished to increase the fractional
conversion of a CSTR, they can decrease the volumetric flow rate, keeping the
volume of reactor constant, to increase the space time.
8.15 Glossary 303
8.15 Glossary
row vector: In MATLAB, a matrix that has only one row.
transpose: An operation that transforms the rows of a matrix into columns (or
the other way around, if you prefer).
8.16 Exercises
Exercise 8.1 A mixture contains n-pentane (1), n-hexane (2), and n-heptane (3) at equal
mole fractions (z 1 = z 2 = z 3 ), where the term in parentheses corresponds to the compo-
nent index we will use. The temperature is 55 ◦ C. The pressure is adjusted so that 75%
of the mixture is vapor (V /F = 0.75) while the temperature remains constant at 55 ◦ C.
Determine the pressure and compositions of the two phases.
At 55 ◦ C the vapor pressure of n-pentane, n-hexane, and n-heptane are P 1sat = 1.903
bar, P 2sat = 0.644 bar, and P 3sat = 0.231 bar, respectively.
y 1 P = x 1 P 1sat
y 2 P = x 2 P 2sat
y 3 P = x 3 P 3sat
z1 F = y 1V + x1 L
z2 F = y 2V + x2 L
z3 F = y 3V + x3 L
1 = y1 + y2 + y3
1 = x1 + x2 + x3
8.16 Exercises 305
Then to get our final expression, we divide through our mole balances by F , giving:
y 1 P = x 1 P 1sat
y 2 P = x 2 P 2sat
y 3 P = x 3 P 3sat
z 1 = y 1 V /F + x 1 L/F
z 2 = y 2 V /F + x 2 L/F
z 3 = y 3 V /F + x 3 L/F
1 = y1 + y2 + y3
1 = x1 + x2 + x3
This results in a system of 8 equation with 8 unknowns which we can solve. Our 8
unknowns are: y 1 , y 2 , y 3 , x 1 , x 2 , x 3 , L/F and P . When you solve, note that all of the
unknowns except P that we will solve for will be between 0 and 1. Also, using our values of
P isat in bar, our computed P will also be in bar. Solving, I obtain: y 1 = 0.4055, y 2 = 0.3463,
y 3 = 0.2482, x 1 = 0.1167, x 2 = 0.2946, x 3 = 0.5887, L/F = 0.25, and P = 0.5479 bar.
Now just think, with this code you can become a design engineer.
Exercise 8.2 Reactants A and B can react irreversibly to produce either a desired product,
D, or an undesired product, U, as shown in the equations below:
A +B → D (1)
A +B →U (2)
The corresponding rate expressions are given by:
½ ¾
−15300J/mol
r 1 = 1.12 × 102 min−1 exp
¡ ¢
CA
RT
½ ¾
−23700J/mol
r 2 = 1.87 × 102 min−1 exp
¡ ¢
CB
RT
A liquid feed mixture containing 10 mol A per gallon and 12 mol B per gallon at 350 K
is fed to a 25 gallon isothermal CSTR (operating at 350 K) at a rate of 12.5 gallons per
minute.
What is the conversion of the limiting reagent? What is the outlet selectivity (in mol
D per mol U)? The limiting reagent will be the reactant that will be consumed first. Here,
for each mol of A consumed a mol of B is consumed. The limiting reagent will therefore
be the species with the smallest concentration in the feed. Why would the conversion
with respect the limiting reagent be preferred?
For this set of reactions, the following general system of mole balance design equa-
tions will apply:
τ ν A,1 r 1 + ν A,2 r 2 = C A −C A0
¡ ¢
τ νB,1 r 1 + νB,2 r 2 = C B −C B0
¡ ¢
0
τ νD,1 r 1 + νD,2 r 2 = C D −C D
¡ ¢
306 Chapter 8 Systems of Equations
0
τ νU ,1 r 1 + νU ,2 r 2 = CU −CU
¡ ¢
Exercise 8.3 Please solve the following system of 10 equations with 10 unknowns:
7x 1 + 4x 2 + 3x 3 + 8x 4 + 8x 5 + 4x 6 + 1x 7 + 2x 8 + 5x 9 + 1x 10 =4
4x 2 + 7x 3 + 3x 4 + 3x 5 + 8x 6 + 1x 7 + 8x 8 + 1x 9 + 10x 10 =9
3x 1 + 8x 2 + 7x 3 + 5x 4 + 8x 5 + 6x 6 + 5x 7 + 3x 8 + 2x 9 =2
8x 2 + 2x 3 + 7x 4 + 2x 5 + 5x 6 + 8x 7 + 5x 8 + 9x 9 + 8x 10 =3
1x 1 + 2x 2 + 1x 3 + 9x 4 + 9x 5 + 9x 6 + 9x 7 + 2x 8 + 2x 9 + 8x 10 =1
8x 1 + 5x 2 + 5x 3 + 10x 4 + 3x 5 + 3x 6 + 1x 7 + 6x 8 + 8x 9 + 9x 10 =1
7x 1 + 4x 2 + 10x 3 + 5x 4 + 2x 5 + 8x 6 + 6x 7 + 3x 8 + 5x 9 + 1x 10 =9
3x 1 + 6x 2 + 3x 3 + 1x 4 + 3x 5 + 8x 6 + 5x 7 + 7x 8 + 10x 9 + 4x 10 =6
10x 1 + 7x 2 + 6x 3 + 1x 4 + 6x 5 + 4x 6 + 7x 8 + 1x 9 + 3x 10 =5
8x 2 + 2x 3 + 3x 4 + 5x 5 + 6x 6 + 3x 7 + 7x 8 + 4x 9 + 8x 10 =1
x 1 = −0.8120
x 2 = −2.3182
x 3 = −8.7617
x 4 = 13.8025
x 5 = −9.2603
x 6 = 4.7159
x 7 = 2.1875
x 8 = 22.3778
x 9 = −8.4912
x 10 = −14.4467
Exercise 8.4 We have seen how we can use fsolve to model binary vapor/liquid equi-
librium. In Listing 8.6 we specified temperature and the liquid-phase mole fraction, and
solved for the pressure and vapor-phase mole fraction. This was applied to the binary
system of hexane(1)/cyclohexane(2). Here, let’s go a step further and construct a px y
phase diagram. What do we mean by this? Well, temperature is known and constant.
x 1 spans the range 0 to 1. So loop over values of x 1 over the range 0 to 1, and solve for
the corresponding y 1 and pressure. Then plot p vs x 1 and p vs y 1 on the same plot. You
should connect the points with a line to create a phase-envelope. The more points you
use, the smoother your curve.
In the table below, I provide reference data from DDBST at 343.15 K. Perform your
calculations at this temperature. Then on the same plot, add the reference data as sym-
bols. How do the results compare? To your plot, please add a title with the system name
(hexane(1)/cyclohexane(2)) and temperature, axis labels, and a legend to distinguish
between your Raoult’s law predictions and the reference data.
8.16 Exercises 307
p/kPa x1 y1
77.327 0.12500 0.17900
81.607 0.25000 0.33600
85.673 0.37500 0.46500
90.033 0.50000 0.59000
94.019 0.62500 0.70800
97.965 0.74200 0.80600
100.818 0.87500 0.90100
Here I have copied our table of Antoine parameters, where T is in ◦ C and p is in
mmHg.
Exercise 8.5 Continuing from the previous exercise, in Listing 8.10 we specified pressure
and the liquid-phase mole fraction, and solved for the temperature and vapor-phase
mole fraction. This was applied to the binary system of hexane(1)/cyclohexane(2). Here,
let’s go a step further and construct a T x y phase diagram. What do we mean by this?
Well, pressure is known and constant. x 1 spans the range 0 to 1. So loop over values of x 1
over the range 0 to 1, and solve for the corresponding y 1 and temperature. Then plot T vs
x 1 and T vs y 1 on the same plot. You should connect the points with a line to create a
phase-envelope. The more points you use, the smoother your curve.
In the table below, I provide reference data from DDBST at 101.33 kPa. Perform your
calculations at this pressure. Then on the same plot, add the reference data as symbols.
How do the results compare? To your plot, please add a title with the system name (hex-
ane(1)/cyclohexane(2)) and pressure, axis labels, and a legend to distinguish between
your Raoult’s law predictions and the reference data. A table of Antoine parameters is
provided in the previous exercise.
T /K x1 y1
353.87 0.00000 0.00000
352.65 0.08200 0.12100
352.15 0.12100 0.16700
351.35 0.16700 0.22600
350.50 0.22600 0.30000
349.90 0.27000 0.35300
349.55 0.30000 0.38400
348.85 0.35300 0.43800
348.55 0.38400 0.46800
347.85 0.43800 0.52300
346.85 0.52300 0.60200
346.05 0.59100 0.67100
345.20 0.67100 0.74000
344.55 0.74000 0.79200
344.00 0.79700 0.84000
343.55 0.84000 0.87700
343.20 0.87700 0.90400
341.85 1.00000 1.00000
308 Chapter 8 Systems of Equations
T x y phase-diagrams are very important for the design of distillation columns. Columns
are typically operated at a constant pressure (typically near atmospheric), and a tempera-
ture gradient is present from the top to the bottom of the column. It can be extremely
useful to visualize such processes using what is called a McCabe-Thiele diagram. In a
McCabe-Thiele diagram, we take all of our data from our T x y, and plot y vs x. Con-
nect the points to create a loci of equilibrium compositions. Then last, as a reference,
construct a y = x line from x = y = 0 to x = y = 1. Give it a try and label your plot.
Exercise 8.6 In Exercises 8.4 and 8.5 we constructed px y and T x y phase diagrams for a
system obeying Raoult’s law. Raoult’s law makes three important assumptions:
Again, if you do not know what this means, no worries, just be sure to sign-up for Chemical
Engineering Thermodynamics before you graduate. The second and third assumptions
are generally very good so long as we are at low pressures. The first assumption is generally
where Raoult’s law breaks down. We account for solution phase non-ideality using activity
coefficients (Lewis/Randall or Raoult’s law normalized), which leads to the “modified”
Raoult’s law expressions. A binary system at vapor/liquid equilibrium is governed by the
following system of non-linear equations:
y 1 p = γ1 x 1 p 1sat
y 2 p = γ2 x 2 p 2sat
where γ1 and γ2 are the activity coefficient of component 1 and 2, respectively. The pure
component vapor pressure is only a function of temperature, and is commonly computed
using Antoine’s equation:
B
log10 p sat = A −
T +C
The activity coefficient is, in theory, a function of temperature, pressure, and composition.
However, for condensed phase systems (liquids) the pressure dependence can typically be
safely ignored. For this problem, this means that γ1 and γ2 are functions only temperature
and composition.
As used in Exercise 7.4, one way to model the activity coefficient is using the NRTL
(non-random two-liquid) equation:
¶2
G 21 τ12G 12
· µ ¸
ln γ1 = x 22 τ21 +
x 1 + x 2G 21 (x 2 + x 1G 12 )2
¶2
G 12 τ21G 21
· µ ¸
ln γ2 = x 12 τ12 +
x 2 + x 1G 12 (x 1 + x 2G 21 )2
where
g 12 − g 22 g 21 − g 11
τ12 = τ21 =
RT RT
and
8.16 Exercises 309
As in Exercise 7.4 we will use the NRTL equation to model the activity coefficients
with the following set of parameters:
5 http://www.ddbst.com/en/EED/VLE/VLE%20Ethanol%3BEthyl%20acetate.php
6 Note that the NRTL parameters we are using were regressed to reference data at 313.15 K.
Using them at a different temperature is an extrapolation. While NRTL does in theory account for
temperature dependence, you should in general proceed with caution.
310 Chapter 8 Systems of Equations
Exercise 8.7 (Two attempts of Professor Paluch discussing how to set-up the problem
can be found here and here.) Now that we have a grasp of CSTR basics, let’s consider the
case of multiple reactions. We will consider two scenarios. First we will have the case of
parallel reactions. For this case, the reactant (or reactants) is consumed by two or more
different reactions. Let’s consider the case:
A→B (P.1)
A →C (P.2)
The second case will be series reactions, in which the reactant (or reactants) form an
intermediate product which reacts further to form another product. For our exercise we
will consider the case:
A→B (S.1)
B →C (S.2)
In both cases, B will be our desired product and C will be an undesired product. When
quantifying reaction progress for the case of series and parallel reactions, a common
reaction progress variable is the selectivity of the desired relative to the undesired product.
Here we define the selectivity of B relative to C (or desired product relative to undesired
product) as
nB C B
S B /C = =
nC C C
How does our mole balance design equation change? Well, before when we had a
single reaction we had
r A = νA r
Now, we just need to sum over each reaction. We will use a subscript 1 to correspond to
reaction 1, and a subscript 2 to correspond to reaction 2 in both the parallel (P) and series
(S) case. (Note that both the rate r and stoichiometic coefficient ν A will depend on the
reaction number. Also note that it is possible to have a stoichiometric coefficient of 0.)
This gives the net rate of reaction for the parallel and series case of:
r A = ν A,1 r 1 + ν A,2 r 2
For this problem, we need to know both n B and nC (or C B and CC ). Remember for
a single reaction the number of moles of each species is related by stoichiometry. This
will not be the case here with our multiple reactions. We will therefore have a system of
equations; a mole balance design equation for component B and C, and since our rate
expression is dependent on A, solve for A too. For both the parallel and series reaction
case we have:
r A = ν A,1 r 1 + ν A,2 r 2
r B = νB,1 r 1 + νB,2 r 2
rC = νC ,1 r 1 + νC ,2 r 2
For reaction 1, use the same parameters as before, namely,
r 1 = k 1C A
E 1 = 15.3kcal/mol
For reaction 2 for the parallel reaction case (P.2),
r 2 = k 2C A
r 2 = k 2C B
In Chapter 9 we learn how to numerically solve first order initial value ordinary
differential equations. Then in Chapter 10 we will solve systems of first order ordi-
nary differential equations, and in Chapter 11 higher order differential equations.
By the end of this chapter you will be able to:
If you work through the chapter and believe these goals are not met, please
re-review the material and reach out for help.
df
=af (9.1)
dt
where a is a constant that characterizes how quickly the population increases.
Notice that both sides of the equation are functions. To say that two functions
are equal is to say that their values are equal at all times. In other words:
313
314 Chapter 9 Ordinary Differential Equations
df
∀t : (t ) = a f (t )
dt
This is an ordinary differential equation (ODE) because all the derivatives in-
volved are taken with respect to the same variable. If the equation related deriva-
tives with respect to different variables (partial derivatives), it would be a partial
differential equation. This equation is first order because it involves only first
derivatives. If it involved second derivatives, it would be second order, and so on.
This equation is linear because each term involves t , f or d f /d t raised to the
first power; if any of the terms involved products or powers of t , f and d f /d t it
would be nonlinear.
Linear, first order ODEs can be solved analytically; that is, we can express
the solution as a function of t . This particular ODE has an infinite number of
solutions, but they all have this form:
f (t ) = be at
For any value of b, this function satisfies the ODE. If you don’t believe me, take its
derivative and check.
If we know the population of bacteria at a particular point in time, we can use
that additional information to determine which of the infinite solutions is the
(unique) one that describes a particular population over time.
For example, if we know that f ((0) = 5 billion cells, then we can write
f (0) = 5 = be a0
and solve for b, which is 5. That determines what we wanted to know:
f (t ) = 5e at
The extra bit of information that determines b is called the initial condition
(although it isn’t always specified at t = 0). So to put it all together, we just solved
a linear, first order, initial value ODE.
Unfortunately, most interesting physical systems are described by nonlinear
DEs, most of which can’t be solved analytically. The alternative is to solve them
numerically. In this chapter we will solve first order, initial value ODEs. Then in a
couple of chapter we will solve second (and higher) order, initial value ODEs.
If you would like to learn more about DEs, the “Open Textbook Library” con-
tains a number of excellent, freely available textbooks on the subject.1 For a quick
introduction or refresher geared toward engineering students, I would recom-
mend “Notes on Diffy Qs: Differential Equations for Engineers”, by Jirí Lebl which
is actively updated and under development, and is freely available.
9.2 Euler’s method 315
df
(t ) = g (t , y)
dt
where g is some function that maps (t , y) onto r ; that is, given the time and
current population, it computes the rate of change. Then we can advance from
one point in time to the next using these equations:
Tn+1 = Tn + ∆t (9.2)
F n+1 = F n + g (t , y) ∆t (9.3)
Here {Ti } is a sequence of times where we estimate the value of f , and {F i } is the
sequence of estimates. For each index i , F i is an estimate of f (Ti ). The interval
∆t is called the time step.
Assuming that we start at t = 0 and we have an initial condition f (0) = y 0
(where y 0 denotes a particular, known value), we set T1 = 0 and F 1 = y 0 , and then
use Equations 9.2 and 9.3 to compute values of Ti and F i until Ti gets to the value
of t we are interested in.
If the rate doesn’t change too fast and the time step isn’t too big, Euler’s method
is accurate enough for most purposes. One way to check is to run it once with
time step ∆t and then run it again with time step ∆t /2. If the results are the same,
they are probably accurate; otherwise, cut the time step again.
Euler’s method is first order, which means that each time you cut the time
step in half, you expect the estimation error to drop by half. With a second-order
method, you expect the error to drop by a factor of 4; third-order drops by 8, etc.
The price of higher order methods is that they have to evaluate g more times per
time step.
After we have solved a few ODE’s using MATLAB’s built-in functionality, we
will revisit Euler’s method and see it in action in Section 9.10.
1 https://open.umn.edu/opentextbooks/subjects/mathematics
316 Chapter 9 Ordinary Differential Equations
9.4 ode45
t If you are using GNU Oc- A limitation of Euler’s method is that the time step is constant from one iteration
tave, you will need to in- to the next. But some parts of the solution are harder to estimate than others; if
stall the “ode” package.
the time step is small enough to get the hard parts right, it is doing more work than
You will then need to add
pkg load ode to your
necessary on the easy parts. The ideal solution is to adjust the time step as you
scripts or execute it from go along. Methods that do that are called adaptive, and one of the best adaptive
the Command Window before methods is the Dormand-Prince pair of Runge-Kutta formulas. You don’t have to
using ode45. If you are un- know what that means, because the nice people at Mathworks have implemented
sure if you currently have it in a function called ode45. The ode stands for “ordinary differential equation
the ode package installed, [solver];” the 45 indicates that it uses a combination of 4th and 5th order formulas.
execute pkg list from the
Command Window .
9.4 ode45 317
In order to use ode45, you have to write a MATLAB function that evaluates g as
a function of t and y. As an example, suppose that the rate of population growth
for rats depends on the current population and the availability of food, which
varies over the course of the year. The governing equation might be something
like
df
((t ) = a f (t ) [1 + sin (ωt )] (9.4)
dt
where t is time in days and f (t ) is the population at time t . a and ω are parame-
ters. A parameter is a value that quantifies a physical aspect of the scenario being
modeled. For example, in Example 7.2 we used parameters rho and r to quantify
the density and radius of a duck. Parameters are often constants, but in some
models they vary in time. In this example, a characterizes the reproductive rate,
and ω is the frequency of a periodic function that describes the effect of varying t Our bacteria growth equa-
food supply on reproduction. tion (eq. 9.1) and rat growth
equation (eq. 9.4) should
This equation specifies a relationship between a function and its derivative.
look very similar. In the rat
In order to estimate values of f numerically, we have to transform it into a rate problem we multiply a f by
function. The first step is to introduce a variable, y, as another name for f (t ) a term that oscillates with t
to model the change in food
df
(t ) = a y [1 + sin (ωt )] supply with seasons.
dt
This equation means that if we are given t and y, we can compute d f /d t (t ),
which is the rate of change of f . The next step is to express that computation as a
function called g :
¡ ¢
g t , y = a y [1 + sin (ωt )]
Writing the function this way is useful because we can use it with Euler’s method
or ode45 to estimate values of f . All we have to do is write a MATLAB function that
evaluates g . Here’s what that looks like using the values a = 0.01 and ω = 2π/365
(one cycle per year):
You can test this function from the Command Window by calling it with different
values of t and y; the result is the rate of change (in units of rats per day):
>> r = rats(0, 2)
r = 0.0200
318 Chapter 9 Ordinary Differential Equations
So if there are two rats on January 1, we expect them to reproduce at a rate that
would produce 2 more rats per hundred days. But if we come back in April, the
rate has almost doubled:
>> r = rats(120, 2)
r = 0.0376
Since the rate is constantly changing, it is not easy to predict the future rat
population, but that is exactly what ode45 does. Here’s how you would use it:
t Something we will do in
later examples, you might >> ode45(@rats, [0, 365], 2)
store the time interval and
the initial condition to vari-
ables, say t_range and ic, The first argument is a handle for the function that computes g , the rate. The
to clarify your code. second argument is the interval we are interested in, one year. The third argument
is the initial population, f (0) = 2.
When you call ode45 without assigning the result to a variable, MATLAB dis-
plays the result in a figure. Giving the figure a title and labeling the axis:
>> title('rats')
>> xlabel('day')
>> ylabel('Number of rats')
rats
80
70
60
50
Number of rats
40
30
20
10
0
0 50 100 150 200 250 300 350 400
day
rate of growth is slow in the winter and summer, and faster in the spring and fall,
but it also accelerates as the population grows.
Within this chapter I will define parameters within the rate function M-file.
However, remember that this is a case where the use of anonymous functions
may be advantageous. If we wanted to look at the effect of the parameters a or ω
on the solution, we would update their value in the rate function, save, and then
re-run. We could alternatively create a general rate function that takes as input
variables the parameters a and ω:
This rate function can not be used directly with ode45 since it expects a (rate)
function with input variables of just t and y. We can overcome this limitation
using an anonymous function:
>> a = 0.01;
>> omega = 2 * pi / 365;
>> rats_gen = @(t,y) rats_general(t,y,a,omega);
>> ode45(rats_gen, [0, 365], 2)
In doing so we find we obtain the same result. Now if you wished to change the
value of one of the parameters, we could update its value in the Command Window ,
re-define rats_gen, and then re-solve.
The first return value is assigned to T; the second is assigned to Y. Each element
of T is a time, t , where ode45 estimated the population; each element of Y is an
estimate of f (t ). The variables T and Y are column vectors.
If you assign the output values to variables, ode45 doesn’t draw the figure;
you have to do it yourself:
>> title('rats')
>> xlabel('day')
>> ylabel('Number of rats')
The resulting plot is the same as fig. 9.1. In the plot you will notice that the space
between the points is not quite even. They are closer together at the beginning of
the interval and farther apart at the end.
To see the population at the end of the year, you can display the last element
from each vector:
Each time rats_2 is called, it plots one data point; in order to see all of the data
points, you have to use hold on. To make the resulting plot clear and readable,
9.7 What can go wrong? 321
>> clf
>> hold on
>> [T, Y] = ode45(@rats_2, [0, 70], 2);
>> plot(T,Y,'bo')
5.5
4.5
3.5
2.5
2
0 10 20 30 40 50 60 70
g (t , y) = a y
You might be tempted to write this:
But that would be wrong. So very wrong. Why? Because when ode45 calls
rate_func, it provides two arguments. If you only take one input variable, you’ll
get an error. So you have to write a function that takes t as an input variable, even
if you don’t use it.
This might be a scary message, but if you read the first line and ignore the rest,
you’ll get the idea.
Yet another mistake that people make with ode45 is leaving out the brackets
on the second argument. In that case, MATLAB thinks there are four arguments,
and you get
Again, if you read the first line, you should be able to figure out the problem
(tspan stands for “time span”, which we have been calling the interval).
9.8 Stiffness
There is yet another problem you might encounter, but if it makes you feel better, t Computing speed and the
it might not be your fault: the problem you are trying to solve might be stiff.2 I efficiency of MATLAB keeps
improving, so this example
won’t give a technical explanation of stiffness here, except to say that for some
might not be too bad on
problems (over some intervals with some initial conditions) the time step needed
your computer. Note, when
to control the error is very small, which means that the computation takes a long MATLAB is struggling, a
time. Here’s one example: Stop icon will show up in
the bottom left corner of
df the plot window. You can
= f 2− f 3
dt click it to kill the process.
If you solve this ODE with the initial condition f (0) = δ over the interval from 0 to You can also click Ctrl + c
2/δ, with δ = 0.01, you should see something like this: from the command line to
kill a process. You may no-
2 The following discussion is based partly on an article from Mathworks available at https: tice Stop buttons in a few
//www.mathworks.com/company/newsletters/articles/stiff-differential-equations.html plots in this chapter where
we try computationally
intensive processes.
324 Chapter 9 Ordinary Differential Equations
1.2
0.8
0.6
0.4
0.2
0
0 50 100 150 200
9.9 Examples
The best way to get comfortable using ode45 is to solve some problems. And the
best problems to practice on are ones that you know the answer to. In addition
to the examples here, for more practice I would encourage you to solve some of
the example problems with analytical solutions provided in “Notes on Diffy Qs:
Differential Equations for Engineers”.
Example 9.1 (Link to screen cast with accompanying M-files.) Pretend you
dropped an object from a high stationary platform (or from an airplane).
Let’s model the downward velocity of the object as a function of time. If
frictional resistance is ignored, we have
dv
ma(t ) = m (t ) = mg (9.5)
dt
where a is the acceleration of the object, v is the downward velocity, t is
time, g is the acceleration due to gravity, and m is the mass. If we account
for the frictional force on the object due to air, which acts opposite in
direction to g , and we assume that the frictional force is proportional to v,
performing a force balance we have
dv
ma(t ) = m (t ) = mg − γv(t ) (9.6)
dt
where γ is our coefficient of friction. (We previously included m in our
expression to facilitate the writing of our force balance.)
Dividing through by m and letting k = γ/m, we have
dv
(t ) = g − kv(t ) (9.7)
dt
subject to the initial condition
v(0) = 0 (9.8)
We could solve this expression using separation of variables and obtain the
analytic result
g³ ´
v(t ) = 1 − e −kt (9.9)
k
Let’s solve the differential equation numerically and compare to the analytic
solution. To solve, let g = 9.81 m/s2 and k = 0.5 s−1 . To solve, let’s first create
our function. We start by re-writing the expression replacing v(t ) with y
326 Chapter 9 Ordinary Differential Equations
dv
(t ) = g − k y (9.10)
dt
dv
Next, replace d t (t ) with g (t , y)
g (t , y) = g − k y (9.11)
Careful not to confuse g (t , y) with parameter g . We are now set to write our
function and solve over the range 0 to 20 seconds.
20
18
16
14
12
position/m
10
0
0 5 10 15 20
t/s
>> fplot(@free_fall_soln,[0,20],'-r')
20
18
16
14
12
position/m
10
0
0 5 10 15 20
t/s
A perfect match! This is also a cool problem because after about 10 s we see
the velocity appears to plateau out at a constant value. When the velocity
becomes constant, the acceleration is 0 and we call this the terminal velocity.
This is what makes skydiving so much fun; if the acceleration is 0, the net
force is also 0.
As an aside, while I created a separate M-file for free_fall_soln, this is a
perfect case when using an anonymous function could be convenient.
>> k = 0.5;
>> g = 9.81;
>> free_fall_soln = @(t) (g/k)*(1-exp(-k*t));
>> fplot(free_fall_soln,[0,20],'-r');
328 Chapter 9 Ordinary Differential Equations
Example 9.2 (Link to screen cast with accompanying M-file.) The first
differential equation I ever remember solving was the draining of a tank. I
also remember solving this problem when I taught Fluid Mechanics.
Imagine we have a cylindrical tank filled with a liquid. Then at time t = 0 a
valve is opened at the bottom of the tank allowing the liquid to drain. We
can perform a mass balance on the tank just like in your Mass and Energy
Balance course.
dm
(t ) = −ṁ(t ) (9.14)
dt
where m is the total mass of fluid in the tank, t is time, and ṁ is the mass
flow rate of fluid out of the tank. Assuming constant uniform density (ρ)
d ρV
¡ ¢
(t ) = −ρQ(t ) (9.15)
dt
dV
(t ) = −Q = −av(t ) (9.16)
dt
where in the second expression we have canceled out ρ, and Q is the volu-
metric flow rate which we can relate to the velocity of the fluid at the exit
(v) and the area of the hole through which the fluid is draining.
Performing an energy balance, we can relate the kinetic energy of the fluid
at the exit to the potential energy of the fluid at the top of the tank
1
mg h = mv 2 (9.17)
2
where g is the acceleration due to gravity and h is the height of the fluid in
the tank. Solving for v
¡ ¢1/2
v = 2g h (9.18)
dh ¢1/2 ¡ ¢1/2
h(t )1/2
¡
A (t ) = −a 2g h(t ) = −a 2g (9.19)
dt
¡ ¢1/2
dh −a 2g
(t ) = h(t )1/2 (9.20)
dt A
If we were to solve analytically using separation of variables, subject to the
initial condition h(0) = h 0 , the result is
¶2
t
µp
h(t ) = h0 − k (9.21)
2
¡ ¢1/2
where k = a 2g /A.
Let’s solve numerically. Start by replacing h(t ) with y
¡ ¢1/2
dh −a 2g
(t ) = y 1/2 (9.22)
dt A
dh
Next, replace d t (t ) with g (t , y)
¡ ¢1/2
−a 2g
g (t , y) = y 1/2 (9.23)
A
We can now write our function
Listing 9.7 tank_drain.m
In writing the function, I have lumped all of the parameters into k and have
set its value to 0.4 for the purpose of this problem. Solving
>> hold on
>> [T,Y] = ode45(@tank_drain, [0,20], 20);
>> plot(T,Y,'ko')
>> xlabel('time')
>> ylabel('height')
330 Chapter 9 Ordinary Differential Equations
20
18
16
14
12
height
10
0
0 5 10 15 20
time
Where the last input to ode45 is our initial condition h(0) = 20 m which I
have arbitrarily chosen for our solution. Okay, and now let’s compare to the
analytic solution.
>> fplot(@tank_drain_soln,[0,20],'-r')
9.10 Euler in action! 331
20
18
16
14
12
height
10
0
0 5 10 15 20
time
A perfect match!
Once again, I could just as well use an anonymous function for this last part.
>> k = 0.4;
>> h0 = 20;
>> tank_drain_soln = @(t) (sqrt(h0)-k*t./2).^(2);
>> fplot(tank_drain_soln,[0,20],'-r');
Now let’s apply it to solve the rat growth problem (Listing 9.1).
>> hold on
>> t0=0; tfinal=365; dt=1; y0=2;
>> [T1,Y1]=euler_ode(@rats,[t0,tfinal],dt,y0);
>> plot(T1,Y1,'r-')
We are using a time step of 1 day, and are plotting the solution as a red line. Note
that in euler_ode we are returning two vectors, T and Y; if you wish to return
multiple variables from a function, we use the bracket notation as done here. Let’s
also try a time step of 0.1 days and plot the result as a green line.
>> dt=0.1;
>> [T2,Y2]=euler_ode(@rats,[t0,tfinal],dt,y0);
>> plot(T2,Y2,'g-')
Lastly, let’s compare our answer to that using ode45, where I will plot the solution
as blue circles.
9.10 Euler in action! 333
Notice that with ode45 we do not need to specify a time step, it is an adaptive
method. All of the other required inputs are the same.
The plot comparing our results is shown below. We find that using Euler’s
method with a timestep of 1 day, we are in good agreement with ode45 at short
times, but then the disagreement increases as time increases. Initially, the error is
very small and not noticeable. The error then grows because F n+1 is dependent
on F n , the value at the previous integration step. This causes errors to propagate
and grow.
When we decrease the time step to 0.1 days, the agreement is excellent.
80
70
60
50
40
30
20
10
0
0 50 100 150 200 250 300 350 400
Figure 9.8 Comparing Euler’s method and ode45 to solve the rat growth
problem.
The error in Euler’s method is proportional to the time step size. So we can
always decrease the value of the time step and improve its accuracy. But this
comes at a greater computational cost.
Using ode45 we have the initial condition plus 44 integration steps, but remember
these higher order schemes are evaluating g more times per step than this number
reveals. Using Euler’s method with a timestep of 1 day we have the initial condition
plus 365 integration steps, and with a time step of 0.1 days we have the initial
334 Chapter 9 Ordinary Differential Equations
condition plus 3,650 integration steps! Be that as it may, Euler’s method is rather
robust and efficient, so at times you may find it a good choice to use.
To highlight the speed and robustness of Euler’s method, let’s look at our “stiff”
problem (Listing 9.4). Let’s consider time steps of size delta and delta/10. And
we can compare to ode23s for completeness.
>> hold on
>> delta = 0.01; t0=0; tfinal=200; y0=delta;
>> dt = delta;
>> [T1,Y1] = euler_ode(@stiff,[t0,tfinal],dt,y0);
>> plot(T1,Y1,'r-')
>> dt = delta/10;
>> [T2,Y2] = euler_ode(@stiff,[t0,tfinal],dt,y0);
>> plot(T2,Y2,'g-')
>> [T3,Y3] = ode23s(@stiff, [t0,tfinal], y0);
>> plot(T3,Y3,'bo')
1.2
0.8
0.6
0.4
0.2
0
0 50 100 150 200
Figure 9.9 Comparing Euler’s method and ode23s to solve our “stiff” ODE.
A perfect match! And both sets of Euler calculations computed very quickly.
As a final point, let me just mention that methods like Euler’s method with
fixed time steps have some real applicability, and is something my research group
uses routinely. My group frequently uses a technique called molecular dynamics,
where we are literally solving Newton’s equations of motion at the molecular level.
However, a key requirement of our integration scheme (among others) is that it is
time reversible. That is, from a given point we can rewind back to the beginning.
Fixed time step methods satisfy this condition, while adaptive methods do not.
9.11 Return of fzero 335
>> t0 = 0;
>> tfinal = 365;
>> y0 = 2;
>> [T,Y] = ode45(@rats, [t0, tfinal], y0);
we obtain two vectors, T and Y. T is a vector containing the times at which our
function was evaluated, where the first entry is t0 and the final entry is tfinal.
Y is a vector containing the values of the function at the corresponding time in
vector T. We know that the population is increasing from a value of y0 at time
t0. The first approach we could therefore apply to find when the population is
equal to 30 would be to perform a logical comparison to see when Y is greater
than or equal to 30. The resulting logical vector could then be used to identify
all of the elements of Y that are greater than or equal to 30. We would then take
the first element of the resulting vector as an estimate of the solution. The first
element because the population is increasing with time. Note, we do not perform
a logical comparison to see when Y is equivalent to 30 since ode45 is solving for
the population at discrete times, so an exact value of 30 between t0 and tfinal
is unlikely. Let’s do it!
We see that the population is not exactly 30, but rather 32.8834. We could of course
find a more precise result. One way would be to perform a linear interpolation
between this point and the previous point that would have been just less than 30.
We will return to this point in a couple of chapters when we look at MATLAB’s
built-in interpolating functions. Here, we will use what we know and use fzero.
Think back to Chapter 7 and the use of fzero and our bisection code to find
the zero of a (linear or) non-linear function over a given range. How did these
methods work? Assume that t is our independent variable. We first need to know
two values of t that bracket when the function (or dependent variable) is equal
to 0. We evaluate the function at these two points, and then guess a value of t
(between our other two t ’s that bracket the solution) where the zero occurs, and
evaluate the function at this point. We then check to see which of the original
t ’s bracket the zero with this new guessed value of t . We keep repeating and
shrinking the range until we converge on our answer.
So in order to be able to use fzero, we need a function that we pass a value of
t , and it returns the value of the function. We can do this. If I use ode45 to solve
our ODE over the range 0 to t , and store the results to a vector, the final value of
the vector is the value of the function at t . Let’s create a new function rat_time
to do just that.
>> rat_time(150)
ans = 26.2171
Nice! So now we have a method to compute the value of the function at an exact
time. For fzero, we can only search for where a function is equal to zero. If we
wish to find where the population is equal to 30, we should therefore subtract
30 from the value of the population so that when the population is equal to 30,
9.11 Return of fzero 337
the result of our function is 0. Since the population is increasing, we can be sure
it satisfies the conditions required by fzero. (Think of the bisection method.)
Updating our function and then testing:
>> rat_time_2(150)
ans = -3.7829
Okay. So now let’s use fzero to search over the range 0.1 to 365 for when the
function is 0. Note that we can not begin our search at 0 because this would
result in calling ode45 with an initial and final time of 0, which results in an error.
(There would be nothing to integrate!)
>> fzero(@rat_time_2,[0.1,365])
ans = 159.2232
We get a precise answer of 159.2232 days. We can check by computing the popu-
lation at this time.
>> rat_time(ans)
ans = 30.0000
Beautiful! We find that the answer found from our simple search of 166.0951 was
a reasonable estimate, but it is in error.
When solving, note that I created rat_time_2 as its own M-file. This is an-
other case where it may be attractive to instead use an anonymous function.
Examples 9.1 and 9.2. We will also consider a new example, modeling the cooling
of a cup of coffee.
Example 9.3 (Link to screen cast with accompanying M-file.) Let’s revisit
our free fall example, Example 9.1. What is the terminal velocity. Addition-
ally, at what time is the velocity equal to 15 m/s?
Let’s start by writing a function to compute the velocity at a specified time.
From our graph, we see that after about 15 s the velocity reaches a constant
value. Put differently, the derivative of the velocity (and hence the accel-
eration) is equal to zero. When this occurs we have reached our terminal
velocity.
>> free_fall_time(15)
ans = 19.6091
>> free_fall_time(20)
ans = 19.6191
>> free_fall_time(30)
ans = 19.6200
>> free_fall_time(40)
ans = 19.6200
>> free_fall_time(400)
ans = 19.6164
>> free_fall_time(500)
ans = 19.6160
>> free_fall_time(1000)
ans = 19.6136
>> free_fall_time(10000)
ans = 19.6113
>> free_fall_time(100000)
ans = 19.6113
>> free_fall_time(1e6)
9.11 Return of fzero 339
ans = 19.6157
>> free_fall_time(1e7)
ans = 19.6118
It appears to take a very long time to reach our terminal velocity, although
this small fluctuation may be attributed to numerical error. Nonetheless,
the rate of change and hence acceleration is very small (if not 0), so if this
was a skydiving problem, it would still be tons of fun. Note that if we were
to analytically solve for the terminal velocity as the point when d v/d t = 0,
we have v = 19.6200. Two more notes on this, is d v/d t is only a function of
v. We could therefore plug values of v into our rate function and find when
the rate (d v/d t = 0) is zero. Taking it a step further, we could use fzero on
a modified rate function where we do not take t as an input.
Now let’s find the time when the velocity is equal to 15 m/s. We need to
update free_fall_time so that it returns a value of zero when the velocity
is equal to 15 m/s.
Let’s search for our solution over the range 0.1 to 30 s. Remember, we can
not have a lower-bound of 0 because this would result in a call to ode45
with an initial and final time of 0.
>> fzero(@free_fall_time_2,[0.1,30])
ans = 2.8923
Example 9.4 Now let’s revisit our tank draining example, Example 9.2. Find
the time at which the height of the fluid in the tank is 15, 10, 5 and 1 m.
Let’s start by creating a function that computes the height of the fluid in the
take at a specified t . We then need to modify the function so that it evalu-
ates to zero at these heights. Since we wish to look at 4 different heights, I
will write a general form of the function in which I can also pass the variable
yfinal which corresponds to the desired final height. I will then use an
anonymous function for each case to pass the the desired value of yfinal.
And so on.
9.11 Return of fzero 341
Example 9.5
Suppose that you are given an 8 ounce cup of coffee at 90 ◦ C and a 1 ounce
container of cream at room temperature, which is 20 ◦ C. You have learned from
bitter experience that the hottest coffee you can drink comfortably is 60 ◦ C.
Assuming that you take cream in your coffee, and that you would like to start
drinking as soon as possible, are you better off adding the cream immediately or
waiting? And if you should wait, then how long?
To answer this question, you have to model the cooling process of a hot liquid in
air. Hot coffee transfers heat to the environment by conduction, radiation, and
evaporative cooling. Quantifying these effects individually would be challenging
and unnecessary to answer the question as posed.
As a simplification, we can use Newton’s Law of Cooling:
df
= −r ( f − e)
dt
where f is the temperature of the coffee as a function of time and d f /d t is its time
derivative; e is the temperature of the environment (assume 20 ◦ C), which is a
constant in this case, and r is a parameter (also constant) that characterizes the
rate of heat transfer.
It would be easy to estimate r for a given coffee cup by making a few measurements
over time. Let’s assume that that has been done and r has been found to be 0.001
in units of inverse seconds, 1/s.
In what follows, I will give detailed instructions for solving this problem. The
intention of this is to help you build-up your code (incremental development
style) for what may be the most complex problem we have solved so far. What we
ultimately would like to know is if there is an optimal time when the cream should
be added to the coffee to minimize the required cooling time. To answer this, you
should solve for the required cooling time for three cases. First, just let the coffee
(black, no cream) cool to the desired temperature. Second, add the cream at t = 0
and then let the coffee cool. And third, add the cream at the end so that once it
is added, we are at the final, desired temperature. Keep this in mind as you read
through the detailed instructions that follow.
• Add a function called rate_func that takes t and y and computes g (t , y).
Notice that in this case g does not actually depend on t ; nevertheless, your
function has to take t as the first input argument in order to work with ode45.
Test your function by adding a line like rate_func(0,90) to coffee, then
call coffee from the Command Window .
• Once you get rate_func(0,90) working, modify coffee to use ode45 to
compute the temperature of the coffee (ignoring the cream) for 60 minutes.
342 Chapter 9 Ordinary Differential Equations
Confirm that the coffee cools quickly at first, then more slowly, and reaches
room temperature (approximately) after about an hour.
• Write a function called mix_func that computes the final temperature of a
mixture of two liquids. It should take the volumes and temperatures of the
liquids as parameters.
In general, the final temperature of a mixture depends on the specific heat
of the two substances. But if we make the simplifying assumption that
coffee and cream have the same density and specific heat, then the final
temperature is (v 1 y 1 + v 2 y 2 )/(v 1 + v 2 ), where v 1 and v 2 are the volumes of
the liquids, and y 1 and y 2 are their temperatures.
Add code to coffee to test mix_func.
• Use mix_func and ode45 to compute the time until the coffee is drinkable
if you add the cream immediately.
• Modify coffee so it takes an input variable t that determines how many
seconds the coffee is allowed to cool before adding the cream, and returns
the temperature of the coffee after mixing.
• Use fzero to find the time t that causes the temperature of the coffee after
mixing to be 60 ◦ C.
• What do these results tell you about the answer to the original question?
Is the answer what you expected? What simplifying assumptions does this
answer depend on? Which of them do you think has the biggest effect? Do
you think it is big enough to affect the outcome? Overall, how confident are
you that this model can give a definitive answer to this question? What might
you do to improve it?
Solution:
What a fun problem! The problem statement is very detailed to help you build-up
your code. I will take you through a number of versions of my code as I build it up.
While I will not show any plots, from the rate expression we see that the hotter the
temperature of our beverage, the greater the rate of heat transfer. So when we add
cream we have a step change (decrease) in our temperature, which then results in
a lower rate of heat transfer. Then the question is, is there an optimal time to add
the cream. We will consider three cases. First, we will just let our coffee cool to the
desired temperature. Second, we can add the cream and then let our coffee cool.
And third, we will add the cream at the end so that once it is added, we are at the
final, desired temperature.
The first series of steps are a matter of incremental development steps to get the
code working. I maintained a copy of the function coffee which I used for these
debugging steps. Here is that final version of my debugged code:
9.11 Return of fzero 343
Before I add cream, first let’s calculate the time required to cool black coffee
(that is, coffee with no cream). I will accomplish this using fzero. To do this,
in coffee_0_fzero the top function is a function with no inputs that simply
calls fzero. Following it we have a new helper function, coffee_0_error_func,
which is our error function to be used with fzero. It takes an argument of time
and returns the temperature of the coffee at that time minus 60. I subtract 60
from the temperature so that when we have our desired temperature of 60 ◦ C, the
function will be equal to (or return a value of) 0. This will allow me to use fzero to
solve.
344 Chapter 9 Ordinary Differential Equations
That is exactly how long it took to cool the coffee when we added the creamer first!
The solution is confirmed by calling coffee_2, where we additionally see that
the coffee (without creamer) is first cooled to 65 ◦ C, and then the mixing process
reduces the temperature an additional 5 ◦ C.
So what is going on? From the rate expression, we know that the rate of cooling is
decreasing as the temperature of the coffee decreases (or equivalently as time in-
creases). However, what is more subtle, is the temperature drop upon the addition
of cream is also decreasing as the temperature of the coffee decreases. When the
coffee is at 90, 80, and 70 ◦ C, the decrease in temperature upon adding creamer is
9.12 Isothermal Batch Reactors 349
the reaction proceeds over some period of time. During this time the system
is perfectly mixed, and no additional material is added or removed. For this
problem, we will add (if the reaction is endothermic) or remove (if the reaction is
exothermic) heat at the same rate it is consumed or generated so that the reaction
proceeds isothermally.
Let’s write a mole balance for our system for component A:
In our problem we are not adding or removing anything, so our expression reduces
to:
GENERATION = ACCUMULATION
The accumulation term is something you may have seen in your Mass and Energy
Balance course if you looked at non-steady state balances, and would take the
form here of:
dnA
ACCUMULATION =
dt
It is the time rate of change of the moles of A in the system.
We will assume that the only way moles of A can be generated/consumed is
via a chemical reaction. If we consider here just a single chemical reaction taking
place:
GENERATION = ν A V r
where ν A is the stoichiometic coefficient of A, ν A r is the per unit volume rate of
generation/consumption of A, and V is the volume of the system. With this our
mole balance design equation becomes:
dnA
= νAV r
dt
We will next look at the problem statement for exercise 9.1 that will appear
at the end of the chapter to aid in our discussion of some common notes about
modeling batch reactors. You will then solve exercise 9.1 on your own.
n 0A − n A
fA =
n 0A
The reaction is first order in A and the rate coefficient obeys the Arrhenius
expression with a pre-exponential term equal to 2.4 × 108 s−1 and an activation
energy of 15.3 kcal/mol. Mathematically this means
r = kC A
k = k 0 exp[−E /(RT )]
k 0 = 2.4 × 108 s−1
E = 15.3kcal/mol
1 dnA
= r A = −kC A
V dt
The volume, V , is referred to as a normalization factor, and is a very common
choice for liquid phase reactions. It makes the rate an intrinsic property. So the
units of C A just need to agree with n A /V . Note too that since we are assuming V
is constant, we can bring it into the differential and write
1 d n A d (n A /V ) dC A
= =
V dt dt dt
Likewise the fractional conversion can be written in terms of C A by multiplying
the top and bottom of the expression by 1/V to give
C A0 −C A
fA =
C A0
352 Chapter 9 Ordinary Differential Equations
n 0A − n A = n B − n B0
n 0A − n A = n B
C A0 −C A = C B
My very last note is on the rate expression itself. Here we have a relatively
simple expression. This is generally not the case. Depending on the proposed
mechanism or if we have a reversible reaction, the expression is much more
complicated. (No worries, MATLAB does not care!) But the point I would like to
make is that you can’t (and never should be expected to) just look at a reaction and
know what the form of the rate of reaction should be. The only way is to propose
a model (in various ways) and test it by fitting to experimentally generated data.
9.13 Glossary 353
9.13 Glossary
differential equation (DE): An equation that relates the derivatives of an un-
known function.
ordinary DE: A DE in which all derivatives are taken with respect to the same
variable.
partial DE: A DE that includes derivatives with respect to more than one variable
linear: A DE that includes no products or powers of the function and its deriva-
tives.
time step: The interval in time between successive estimates in the numerical
solution of a DE.
first order (numerical method): A method whose error is expected to halve when
the time step is halved.
stiffness: A characteristic of some ODEs that makes some ODE solvers run slowly
(or generate bad estimates). Some ODE solvers, like ode23s, are designed
to work on stiff problems.
9.14 Exercises
Exercise 9.1 The conversion of A to B (A → B ) takes place in an aqueous solution in an
isothermal batch reactor. The reactor is charged with 1200 L of a 2 M solution of A at 300
K. (Recall 1 M = 1 mol/L.) Calculate the time required to reach 0.8 fractional conversion
of A. The fractional conversion of A may be computed as
n 0A − n A
fA =
n 0A
where n 0A is the initial number of moles of A (at t = 0) and n A is the number of moles at a
given time.
The reaction is first order in A and the rate coefficient obeys the Arrhenius expression
with a pre-exponential term equal to 2.4×108 s−1 and an activation energy of 15.3 kcal/mol.
Mathematically this means
r = kC A
k = k 0 exp[−E /(RT )]
k 0 = 2.4 × 108 s−1
E = 15.3kcal/mol
Chapter 10
Systems of ODEs
Last chapter we learned how to numerically solve first order initial value ordinary
differential equations. We will build upon this in Chapter 10 and will solve systems
of first order ordinary differential equations. By the end of this chapter you will
be able to:
If you work through the chapter and believe these goals are not met, please
re-review the material and reach out for help.
10.1 Lotka-Voltera
The Lotka-Voltera model describes the interactions between two species in an
ecosystem, a predator and its prey. A common example is rabbits and foxes. The t Notice here I am using
model is governed by the following system of differential equations: Lagrange’s notation for
compactness when writing
systems of differential equa-
tions; R 0 = ddRt and F 0 = ddFt .
R 0 = aR − bRF
(The notation on the right-
F 0 = ebRF − cF hand side corresponds to
Leibniz’s notation. Newton’s
where dot notation also exists, but
is not as common in the
• R is the population of rabbits, undergraduate curriculum;
it is used to designate flow
• F is the population of foxes, rates, but there is never a
mention of Newton.) Note
• a is the natural growth rate of rabbits in the absence of predation, that Lagrange’s notation can
also take the form: R t = ddRt
355 and F t = ddFt .
356 Chapter 10 Systems of ODEs
At first glance you might think you could solve these equations by calling
ode45 once to solve for R as a function of time and once to solve for F . The
problem is that each equation involves both variables, which is what makes this a
system of equations and not just a list of unrelated equations. To solve a system,
you have to solve the equations simultaneously.
Fortunately, ode45 can handle systems of equations. The difference is that
the initial condition is a vector that contains initial values R(0) and F (0), and the
output is a matrix that contains one column for R and one for F .
And here’s what the rate function looks like with the parameters a = 0.1,
b = 0.01, c = 0.1 and e = 0.2:
As usual, the first input variable is time. The second input variable is a vector
with two elements, R(t ) and F (t ). I gave it a capital letter to remind me that it is a
vector. The body of the function includes four paragraphs, each explained by a
comment.
The first paragraph unpacks the vector by copying the elements into scalar
variables. This isn’t necessary, but giving names to these values helps me re-
member what’s what. It also makes the third paragraph, where we compute the
10.1 Lotka-Voltera 357
The semi-colon between the elements of the vector is not an error. It is necessary
in this case because ode45 requires the result of this function to be a column
vector.
Now we can run ode45 like this:
As always, the first argument is a function handle, the second is the time interval,
and the third is the initial condition. The initial condition is a vector: the first
element is the number of rabbits at t = 0, the second element is the number of
foxes.
The order of these elements (rabbits and foxes) is up to you, but you have
to be consistent. That is, the initial conditions you provide when you call ode45
have to be the same as the order, inside lotka, where you unpack the input vector
and repack the output vector. MATLAB doesn’t know what these values mean; it
is up to you as the programmer to keep track.
But if you get the order right, you should see something like this:
358 Chapter 10 Systems of ODEs
120
100
80
Population
60
40
20
0
0 50 100 150 200 250 300 350 400
Time in days
to
res = [drdt, dfdt];
Which is pretty good as error messages go. It’s not clear to me why it needs to be a
column vector, but that’s not our problem.
Another possible error is reversing the order of the elements in the initial
conditions, or in the vectors inside lotka. Again, MATLAB doesn’t know what
the elements are supposed to mean, so it can’t catch errors like this; it will just
produce incorrect results.
>> size(M)
ans = 185 2
>> plot(T, M)
In this context, the colon represents the range from 1 to end, so M(:, 1) means
“all the rows, column 1” and M(:, 2) means “all the rows, column 2.”
>> size(R)
ans = 185 1
>> size(F)
ans = 185 1
>> plot(R, F)
22
20
18
16
14
12
10
4
20 30 40 50 60 70 80 90 100 110
10.4 Examples
The best way to get comfortable using ode45 is to solve some problems. And the
best problems to practice on are ones that you know the answer to. In addition
to the examples here, for more practice I would encourage you to solve some of
the example problems with analytical solutions provided in “Notes on Diffy Qs:
Differential Equations for Engineers”.
10.4 Examples 361
Example 10.1
(Link to screen cast with accompanying M-file.)
Let’s solve the following system of differential equations
x 0 = −y
y 0 = (1.01)x − (0.2)y
x(0) = 0
y(0) = −1
Let’s start by solving this problem analytically, because we can, and hope-
fully it will help you appreciate how awesome numerically solving using
MATLAB is.
We start by noticing
x 00 = (−1.01)x − (0.2)x 0
x(0) = 0
0
x (0) = −y(0) = 1
r 2 + (0.2)r + 1.01 = 0
x(0) = A = 0
Leaving us with
1
x0 = − B e −t /10 sin t + B e −t /10 cos t
10
Now applying our second initial condition
x 0 (0) = B = 1
Now let’s solve numerically using MATLAB. Let’s start by writing a function
to compute the rate of change of x and y with respect to t (i.e., x 0 and y 0 ).
We will solve subject to the initial conditions x(0) = 0 and y(0) = −1, and
let’s solve over the range 0 < t < 50. Note that the way I set-up the function
the first equation is our equation for x and the second is our equation for y.
Let’s solve! I will solve by storing the solution to vector T (time) and matrix
M (first column will be the solution for x and the second column will be the
solution for y). I will plot the solution, compare to our analytic result, and
then plot the phase plot.
0.6
0.4
0.2
x or y
-0.2
-0.4
-0.6
-0.8
-1
0 10 20 30 40 50
t
Let’s compare to our analytic solution. Keeping the plot open and then
>> Ta = linspace(0,50);
>> Xa = exp(-Ta./10).*sin(Ta);
>> Ya = (1/10)*exp(-Ta./10).*(sin(Ta)-10*cos(Ta));
364 Chapter 10 Systems of ODEs
>> plot(Ta,Xa,'bo',Ta,Ya,'ro')
>> legend('x','y','x ref','y ref')
This results in the following plot, where the analytic solution for x is drawn
as blue circles and the analytic solution for y as red circles
0.4
0.2
x or y
-0.2
-0.4
-0.6
-0.8
-1
0 10 20 30 40 50
t
A perfect match!
Now let’s close the plot and plot our phase plot. We will use our numeric
solution.
>> plot(X,Y,'-k')
>> xlabel('x')
>> ylabel('y')
0.8
0.6
0.4
0.2
0
y
-0.2
-0.4
-0.6
-0.8
-1
-0.8 -0.6 -0.4 -0.2 0 0.2 0.4 0.6 0.8 1
x
Example 10.2
(Link to screen cast with accompanying M-file.)
Solve the following system of differential equations
x 0 = 4x − 3y
y 0 = 6x − 7y
x(0) = 2
y(0) = −1
We wont work through the analytic solution to this one, but if you would
like to compare the final answer is
x(t ) = 3e 2t − e −5t
y(t ) = 2e 2t − 3e −5t
366 Chapter 10 Systems of ODEs
Now let’s solve numerically using MATLAB. Let’s start by writing a function
to compute the rate of change of x and y with respect to t (i.e., x 0 and y 0 ).
We will solve subject to the initial conditions x(0) = 2 and y(0) = −1, and
let’s solve over the range 0 < t < 1. Note that the way I set-up the function
the first equation is our equation for x and the second is our equation for y.
Let’s solve! I will solve by storing the solution to vector T (time) and matrix
M (first column will be the solution for x and the second column will be the
solution for y). I will plot the solution and then plot the phase plot.
15
x or y
10
-5
0 0.2 0.4 0.6 0.8 1
t
15
x or y
10
-5
0 0.2 0.4 0.6 0.8 1
t
Now let’s close the plot and plot our phase plot. Or to plot in a separate,
new figure window, begin with the command figure(2).
>> figure(2)
>> X = M(:,1);
>> Y = M(:,2);
>> plot(X,Y)
>> xlabel('x')
>> ylabel('y')
16
14
12
10
8
y
-2
0 5 10 15 20 25
x
Notice that when you typed figure(2), “Figure 2” became active. If you
wanted to return to “Figure 1” and update it, you would switch back to
“Figure 1” as the active figure using the command figure(1). And then if
you wanted to return back to “Figure 2”, use figure(2).
Example 10.3
x 0 = σ(y − x)
y 0 = x(r − z) − y
z 0 = x y − bz
Common values for the parameters are σ = 10, b = 8/3 and r = 28.
Use ode45 to estimate a solution to this system of equations.
370 Chapter 10 Systems of ODEs
(a) The first step is to write a function named lorenz that takes t and V as input
variables, where the components of V are understood to be the current values
of x, y and z. It should compute the corresponding derivatives and return
them in a single column vector.
(b) The next step is to test your function by calling it from the command line
with values like t = 0, x = 1, y = 2 and z = 3? Once you get your function
working, you should make it a silent function before calling ode45.
(c) Assuming that Step 2 works, you can use ode45 to estimate the solution for
the time interval t 0 = 0, t e = 30 with the initial condition x = 1, y = 2 and
z = 3.
(d) Use plot3 to plot the trajectory of x, y and z.
Solution:
Let’s start by writing our rate of change function, here called lorenz.
Before moving on, we can test its use with t = 0, x = 1, y = 2, and z = 3. Remember
that lorenz only takes two arguments, so the values of the function will need to
be passed as a single vector. (You can use either column or row since a single index
is needed in either case.)
>> lorenz(0,[1;2;3])
ans =
10
23
-6
10.4 Examples 371
Now let’s solve the system of ODEs over the time interval t 0 = 0 to t e = 30 subject
to the initial conditions x(0) = 1, y(0) = 2, and z(0) = 3. I will store the results to a
vector and matrix. I will then separate the matrix that contains the values of the
functions x, y and z as a function of time into separate column vectors, which I
can use to make a plot of our solution and of our phase plot.
50
x
40 y
z
30
20
x, y, or z
10
-10
-20
-30
0 5 10 15 20 25 30
t
>> plot3(X,Y,Z,'b-')
>> xlabel('x')
>> ylabel('y')
>> zlabel('z')
50
40
30
z 20
10
0
50
20
0 10
0
-10
y -50 -20 x
In Spring 2018 I had a student discover on accident that MATLAB actually has a
built-in lorenz function. The built-in function is actually a MATLAB GUI, that
solves this exact problem. You need just call the function lorenz without any
inputs. There is actually a lotka function too, but MATLAB does not provide
enough information with regards to how to run it or what exactly it does.
Using Euler’s method to solve our Lotka-Voltera model example with a step
size of 0.1 days:
>> [T,M]=euler_ode_system(@lotka,[0,365],0.1,[100,10]);
>> plot(T,M)
120
100
80
60
40
20
0
0 50 100 150 200 250 300 350 400
Figure 10.11 Using Euler’s method to solve our Lotka-Voltera model exam-
ple.
Which appears to be in good agreement with ode45. Nice! But to be sure, let’s
compare to our results using ode45. The peaks seem to be growing, which isn’t
exactly right.
>> hold on
>> [Tr,Mr]=ode45(@lotka,[0,365],[100,10]);
>> plot(Tr,Mr(:,1),'bo',Tr,Mr(:,2),'ro')
10.6 Isothermal Batch Reactors: Multiple Reactions 375
120
100
80
60
40
20
0
0 50 100 150 200 250 300 350 400
Figure 10.12 Comparing Euler to ode45 with a step size of 0.1 days.
Oh no! The results do differ and appear to get worse with time. Remember, the
errors in the calculation get propagated with time as each step is dependent on
the previous. Let’s try a smaller timestep.
>> [T2,M2]=euler_ode_system(@lotka,[0,365],0.01,[100,10]);
>> plot(T2,M2,'-k')
Perfect!
Now that we have spent some time working with Euler’s method, have you
thought of any ways to improve it? The main limitation is that we evaluate the rate
of change at t 0 , and assume that it is constant over the range t 0 to t 0 + ∆t . This
is correct in the limit that ∆t → 0. (Put differently, we are assuming the function
is linear over this range.) One scheme to improve Euler’s method is to first use
Euler’s method to estimate the value of the function and it’s derivative forward in
time. Then, return to t 0 (or an earlier time) and use these predictions to improve
our estimate of the rate of the function. While I certainly do not give it justice here,
this is the general idea behind the predictor-corrector scheme of numerically
solving ODEs.
an isothermal batch reactor. We will discuss in detail the system here (which is
very similar to exercise 8.7, and then you will perform the analysis on your own
for exercise 10.7.
Okay, so now that we have a grasp of batch reactor basics, let’s consider the
case of multiple reactions. We will have two scenarios. First we will have the case
of parallel reactions. For this case, the reactant (or reactants) is consumed by two
or more different reactions. For our exercise we will consider the case:
A→B (P.1)
A →C (P.2)
The second case will be series reactions, in which the reactant (or reactants) form
an intermediate product which reacts further to form another product. For our
exercise we will consider the case:
A→B (S.1)
B →C (S.2)
When labeling the equations I use the pre-fix “P” and “S” just to remind myself
that the equations correspond to parallel and series reactions, receptively. In our
expressions I will just use the number of the equation.
In both cases, B will be our desired product and C will be an undesired product.
When quantifying reaction progress, we define the selectivity of B relative to C (or
desired product relative to undesired product) as
nB
S B /C =
nC
How does our mole balance design equation change? Well, before when we
had a single reaction we had
dnA
= νAV r
dt
Now, we just need to sum over each reaction. We will use a subscript 1 to corre-
spond to reaction 1 and a subscript 2 to correspond to reaction 2. (Note that both
the rate r and stoichiometic coefficient ν A will depend on the reaction number.
Also note that it is possible to have a stoichiometric coefficient of 0.) This gives
the net rate of reaction for the parallel case:
dnA
= ν A,1V r 1 + ν A,2V r 2
dt
For this problem, we need to know both n B and nC . Remember for a single
reaction the number of moles of each species is related by stoichiometry. This will
not be the case here with our multiple reaction. We will therefore have a system of
ODEs to solve; a mole balance design equation for component B and C, and since
10.6 Isothermal Batch Reactors: Multiple Reactions 377
our rate expression is dependent on A, solve for A too. For the parallel reaction
case we have:
dnA
= ν A,1V r 1 + ν A,2V r 2
dt
d nB
= νB,1V r 1 + νB,2V r 2
dt
d nC
= νC ,1V r 1 + νC ,2V r 2
dt
And for the series reaction case we have the same set of equations. What will
change is the numerical values of the stoichiometric coefficients and the expres-
sion for r 2 .
dnA
= ν A,1V r 1 + ν A,2V r 2
dt
d nB
= νB,1V r 1 + νB,2V r 2
dt
d nC
= νC ,1V r 1 + νC ,2V r 2
dt
Since we are starting with just A, our initial conditions will be:
n 0A = 2400
n B0 = 0
nC0 = 0
For reaction 1 (both parallel and series), use the same parameters as before,
namely,
r 1 = k 1C A
k 1 = k 0,1 exp[−E 1 /(RT )]
k 0,1 = 2.4 × 108 s−1
E 1 = 15.3kcal/mol
For reaction 2 for the parallel reactions (P.2),
r 2 = k 2C A
r 2 = k 2C B
10.7 Glossary
state: If a system can be described by a set of variables, the values of those
variables are called the state of the system.
phase plot: A plot that shows the state of a system as point in the space of possi-
ble states.
trajectory: A path in a phase plot that shows how the state of a system changes
over time.
10.8 Exercises
The first 5 questions are actually one long exercise. However, I have broken it up
into small parts in hopes of guiding you to the final solution. As I have mentioned
previously, I view MATLAB as powerful educational tool. We can set-up a model
for a physical system of interest, and then perform virtual experiments looking at
the effect of perturbing variables. And since we are not concerned about solving
problems analytically, we can hypothesize about how to make our model more
realistic, and then readily test our hypothesis.
R 0 = aR − bRF
F 0 = ebRF − cF
where
• R is the population or rabbits
• F is the population of foxes
• a is the natural growth rate of rabbits in the absence of predation
• c is the natural death rate of foxes in the absence of prey
• b is the death rate of rabbits per interaction with a fox
• e is the efficiency of turning eaten rabbits into foxes
Let’s begin by making sure we have our model set-up correctly. Use the following values
of the parameters: a = 0.1, b = 0.01, c = 0.1, and e = 0.2. Also, assume we initially have
100 rabbits and 10 foxes (R(0) = 100 and F (0) = 10). Solve for the population of rabbits
over the course of 365 days. (Note that day is the units of time used in the model.) While
in reality we can not have a fractional number of rabbits and foxes, on day 365 I find the
model predicts we have 22.6552 rabbits and 6.7519 foxes. Use these numbers to confirm
your code is working correctly before moving on.
10.8 Exercises 379
Exercise 10.2 Plotting the rabbit and fox population over the period of 365 days, we
find they exhibit oscillatory behavior. With this set of parameters, the maximum rabbit
population is the same as the initial population. You will notice that in our model (a) the
growth rate of rabbits in the absence of predation and (c) the death rate of foxes in the
absence of prey are equivalent. What happens if the growth rate of rabbits were to double,
a = 0.2, keeping everything else the same? Plot your results. Comment on the how the
maximum value of the rabbit and fox population change, and also on how the oscillatory
behavior changes. Does this make physical sense?
Restore a back to its original value of a = 0.1. Now modify the death rate of foxes, c. If
we make c larger, then foxes have a shorter life expectancy. And if we make c smaller, then
the foxes live longer. What is the effect of changing c? Does this make physical sense?
Exercise 10.3 A limitation of the Lotka-Voltera model is the rabbit growth term in the
absence of predation. Specifically, if there are no foxes, then
R 0 = aR
Again using a = 0.1 and an initial rabbit population of 100, solve for the population of
rabbits over the course of 365 days.
You will find this corresponds to an exponential growth of rabbits, and after 365
days we have 7.1095 × 1017 rabbits. Wow! The predicted behavior is not physical. While
the population may grow exponentially initially, the population must be restricted by
resource availability.
Exercise 10.4 One way to fix the unreasonable exponential growth of rabbits is to instead
use a two-term logistic growth model of the form
a 2
R 0 = aR − R
k
where k is the carrying capacity of the system. That is, the maximum number of rabbits
that the system can support. Again use a = 0.1 and an initial rabbit population of 100.
Try using k = 150 (a carrying capacity greater than the initial population) and k = 50 (a
carrying capacity less than the initial population). Solve for the population of rabbits over
the course of 365 days. What do the results look like?
Does this model seem more appropriate to you?
Exercise 10.5 Let’s return to our original system of equations, but now let’s replace the
exponential growth term with the two-term logistic growth model:
³ a ´
R 0 = aR − R 2 − bRF
k
F 0 = ebRF − cF
Let’s continue to use the same set of parameters: a = 0.1, b = 0.01, c = 0.1, and e = 0.2.
Also, assume again we initially have 100 rabbits and 10 foxes (R(0) = 100 and F (0) = 10).
Let’s look at the effect of the carrying capacity on the system. In the introduction and
Problem 1, we were effectively looking at the case of k → ∞. For that case we observed
oscillatory behavior. Now use k = 150. Solve for the population of rabbits over the course
of 365 days. Plot the results. How does the behavior of the system change? As a reference
to make sure your code is working correctly, at 365 days I find the population of rabbits
and foxes to be 50.0189 and 6.6493, respectively. To further understand what is going on,
solve for 730 days (2 years) or maybe even 10 years. How cool!
380 Chapter 10 Systems of ODEs
What I find interesting is the effect of the carrying capacity on the long-time popula-
tion of the system. By long-time, let’s solve for a period of 3,650 days (10 years). Try using
value of k of 50, 100, 200, 300, 400, 1000. Plot the population and comment on how the
population after 10 years changes with k.
1. At any time the total population is conserved (i.e., everyone recovers and no one
dies in our model)
2. Susceptible people get sick by interacting with infected people
S 0 = −r SI
0
I = r SI − γI
R 0 = γI
where
• S is the fraction of the population that is susceptible,
• I is the fraction of the population that is infectious,
• R is the fraction of the population that is recovered,
• r is the rate constant representative of how quickly people are leaving the suscepti-
ble group and getting infected,
• γ is the rate constant representative of how quickly infected people are recovering.
10.8 Exercises 381
Exercise 10.7 For the parallel and series reactions described in section 10.6, I would like
you to plot the composition of each species versus time and the selectivity of B relative to
C versus time. For the parallel case, I would like you to consider: k 0,2 = 0.1k 0,1 , k 0,2 = k 0,1 ,
and k 0,2 = 10k 0,1 . For the series case I would like you to consider: k 0,2 = 0.1k 0,1 , k 0,2 = k 0,1 ,
and k 0,2 = 10k 0,1 .
Compare the behavior of parallel and series reactions, and discuss how you might
“optimize” your reactor in both cases. Please summarize your findings in a short report
supported with plots. How do your results compare to that of a CSTR operated with the
same space time? (That is, compare to your results from Exercise 8.7.)
Note: at t = 0, n B = CC = 0. You should therefore only plot the selectivity of B relative
to C versus time for t > 0.
Exercise 10.8 In the last exercise, we reconsidered our systems of reactions from Exer-
cise 8.7, but now used a batch reactor instead of a CSTR. Here we will do the same and
revisit our system of reactions from Exercise 8.2, but again using a batch reactor instead
of a CSTR.
Reactants A and B can react irreversibly to produce either a desired product, D, or an
undesired product, U, as shown in the equations below:
A +B → D (1)
A +B →U (2)
The corresponding rate expressions are given by:
½ ¾
−15300J/mol
r 1 = 1.12 × 102 min−1 exp
¡ ¢
CA
RT
½ ¾
¡ 2 −1
¢ −23700J/mol
r 2 = 1.87 × 10 min exp CB
RT
At t = 0 a 25 gallon batch reactor is charged with a liquid solution containing 10 mol A
per gallon and 12 mol B per gallon at 350 K. The batch reactor is operated isothermally
for 2 minutes.
What is the conversion of the limiting reagent? What is the selectivity (in mol D per
mol U)? The limiting reagent will be the reactant that will be consumed first. Here, for
each mol of A consumed a mol of B is consumed. The limiting reagent will therefore be
the species with the smallest concentration in the feed. Why would the conversion with
respect the limiting reagent be preferred?
Again, compare your results to Exercise 8.2, where the CSTR was operated for an
equivalent space time.
Chapter 11
Second-order systems
Last chapter we learned how to numerically solve systems of first order initial
value ordinary differential equations. We will build upon this in Chapter 11 and
will solve higher order differential equations. The key will be to re-write our higher
order differential equation as a series of first order differential equations. By the
end of this chapter you will be able to:
• Explain how to take higher order ODEs and re-write them as a system of
first order ODEs
If you work through the chapter and believe these goals are not met, please
re-review the material and reach out for help.
383
384 Chapter 11 Second-order systems
Because the first function ends before the second begins, they are at the same
level of indentation. Functions like these are parallel, as opposed to nested. A
nested function is defined inside another, like this:
11.1 Nested functions 385
>> v_virial(440,60,425.125,37.960,0.201)
ans = 313.2382
>> v_virial_2(440,60,425.125,37.960,0.201)
ans = 313.2382
>> v_virial_3(440,60,425.125,37.960,0.201)
ans = 313.2382
11.1 Nested functions 387
1 % res = duck2(x,r,rho)
2 % Function to find the required depth of a duck to remain
3 % buoyant in water. duck2 requires that we pass parameters
4 % r (radius of duck) and rho (density of the liquid), in
5 % addition to the depth (x) which is what we will solve for.
6 function res = duck2( x,r,rho )
7 res = 0.3.*4.*r.^(3)-rho.*(3.*r.*x.^(2)-x.^(3));
8 end
In the problem we were solving for the depth (d ) to which a duck of radius r was
submerged in water of density ρ using fzero. fzero expects a function to which
we pass only a single variable. We therefore specified r and ρ in the function to
solve. In order to generalize the function to allow us to pass r and ρ too, we had
to use an anonymous function (see Section 7.16 on page 227) Here, let’s instead
use a nested function to simplify the task, and to use an unmodified call to fzero. t Remember, an anonymous
To accomplish this, I will create a top function duck_zero to which I will pass r function is restricted to a
single executable statement.
and ρ, and it will return the depth d . I will then nest duck2. In this way, duck2
There is no such restriction
can “see” the value of r and ρ in the top function, so that d is the only variable in a nested function.
that now needs to be passed. To solve for d , I will add the call to fzero to the top
function, and save the result to res to be returned. Let’s do it!
Listing 11.5 duck_zero.m
>> r = 10;
>> rho = 1;
>> d = duck_zero(r, rho)
d = 7.2651
This is in perfect agreement with our previous solution. We will look at an applica-
tion to ode45, which has a restriction of two input variables, later in this chapter.
Application to fsolve is also straightforward.
388 Chapter 11 Second-order systems
F = ma
where F is the net force acting on a object, m is the mass of the object, and a is the
resulting acceleration of the object. In a simple case where the object is moving
along a straight line, F and a are scalars, but in general they are vectors.
Even more generally, if F and a vary in time, then they can be thought of as
functions that return vectors; that is, F is a function and the result of evaluating
F (t ) is a vector that describes the net force at time t . So a more explicit way to
write Newton’s law is
~ (t ) = m~
∀t : F a (t )
The arrangement of this equation suggests that if you know m and a you can
compute force, which is true, but in most physical simulations it is the other way
around. Based on a physical model, you know F and m, and compute a.
So if you know acceleration, a, as a function of time, how do you find the
position of the object, z? Well, we know that acceleration is the second derivative
of position, so we can write a differential equation
z 00 = a
Where a and z are functions of time that return vectors, and z 00 is the second time
derivative of z.
Because this equation includes a second derivative, it is a second-order ODE.
ode45 can’t solve this equation in this form, but by introducing a new variable, v,
for velocity, we can rewrite it as a system of first-order ODEs.
z0 = v
v0 = a
The first equation says that the first derivative of z is v; the second says that the
derivative of v is a.
t Screen casts are available Note that while in this chapter we will focus on examples from Newtonian
for Professor Paluch work- mechanics, the same strategy can be used in general to solve higher order (second
ing through the following
order and higher) ODEs; we can convert the ODE to a system of first order ODEs,
three sections: Freefall, Air
resistance, and Parachute.
which we learned to solve in the previous chapter.
They should be viewed as
a series of screen casts that
build-off of each other.
11.3 Freefall
Let’s start with a simple example, an object in freefall in a vacuum (where there’s
no air resistance). Near the surface of the earth, the acceleration of gravity is
g = −9.8 m/s2 , where here the minus sign indicates that gravity pulls down.
11.3 Freefall 389
If the object falls straight down (in the same direction as gravity), we can
describe its position with a scalar value, altitude. So this will be a one-dimensional
problem, at least for now.
Here is a rate function we can use with ode45 to solve this problem:
The first function is the rate function. It gets t and X as input variables, where
the elements of X are understood to be position and velocity. The return value
from freefall is a (column) vector that contains the derivatives of position and
velocity, which are velocity and acceleration, respectively.
Computing z 0 is easy because we are given velocity as an element of X. The
only thing we have to compute is acceleration, which is what the second function
does. acceleration computes acceleration as a function of time, position and
velocity. In this example, the net acceleration is a constant, so we don’t really have
to include all this information yet, but we will soon.
Here’s how to run ode45 with this rate function:
As always, the first argument is the function handle, the second is the time interval
(30 seconds) and the third is the initial condition: in this case, the initial altitude
is 4000 meters and the initial velocity is 0. So you can think of the “object” as a
skydiver jumping out of an airplane at about 12,000 feet.
Here’s what the result looks like:
390 Chapter 11 Second-order systems
4000
3500
3000
2500
2000
1500
1000
500
-500
0 5 10 15 20 25 30
We can now readily plot position versus time and velocity versus time, each in
their own graph. However, they do have a common x-axis, so it may be nice to
“stack” the graphs. By this, I mean create two graphs, but have them lined up in a
column. Since we have two graphs, we can think of this as a single column with
two rows. Here is how we do it:
>> figure
>> subplot(2,1,1) % First plot in 2 by 1 matrix
>> plot(T,Z,'-r')
>> ylabel('position [m]')
>> subplot(2,1,2) % Second plot in 2 by 1 matrix
>> plot(T,V,'-b')
>> ylabel('velocity [m/s]')
>> xlabel('time [s]')
11.3 Freefall 391
4000
position [m]
2000
-2000
0 5 10 15 20 25 30
0
velocity [m/s]
-100
-200
-300
0 5 10 15 20 25 30
time [s]
Great! Let’s keep going. Let’s update our function so that the fzero call
is contained in the function file. To do this, let’s create a new top function
freefall_contact. The function need not take any input variables, but it will
return the time it takes to make contact.
11.3 Freefall 393
Nice! What’s that, keep going? Okay! Knowing the time at which we make
contact is great, but it would be even cooler if we knew the velocity too. We can
do this two ways, the second will be more efficient than the first. But first, since
we now know the time of contact, we could use this to call ode45 again, use this
value as the final time, and then return the contact time and velocity. Let’s do it!
394 Chapter 11 Second-order systems
Perfect! And since freefall_contact will see the final value of T and M, we could
plot the complete trajectory if we would like.
Running freefall_contact_c MATLAB R2018a additionally prints the fol-
lowing warning message:
396 Chapter 11 Second-order systems
This is a pretty clear warning message, and only started to appear when I up-
graded to MATLAB 2018a. Essentially, if we are using a nested function to share
variables, all shared variables should be created in the outer (parent) function.
And in future releases “should” will become a “must,” so we should get in the
habit of doing so.
I created a variable without assigning a value using empty brackets; this is equiva-
lent to creating an empty matrix. Note that if I had instead used M =1 instead, the
result is unchanged.
As a very final note before moving on, if you wished, you could nest acceleration
within freefall. The advantage of doing so would be that you would no longer
need to pass t, z, and v.
t Screen casts are available
for Professor Paluch work-
11.4 Air resistance ing through the following
three sections: Freefall, Air
To make this simulation more realistic, we can add air resistance (or drag). For resistance, and Parachute.
large objects moving quickly through air, the force due to air resistance, called They should be viewed as
“drag,” is proportional to v 2 : a series of screen casts that
build-off of each other.
F drag = c v 2
Where c is a drag constant that depends on the density of air, the cross-sectional
area of the object and the surface properties of the object. For the purpose of this
problem, let’s say that c = 0.2. To convert from force to acceleration, we have
to know mass, so let’s say that the skydiver (with equipment) weighs 75 kg. The t In your fluid mechanics
acceleration can then be computed using Newton’s second law: a drag = F drag /m. class you likely saw the drag
equation: F drag = 12 ρv 2C D A,
Here’s a version of acceleration that takes air resistance into account (you
where ρ is the density of the
don’t have to make any changes in freefall, I just renamed it freefall_2 so as fluid (here air), C D is the
not to be confused with the case of no drag). Recall that while gravity is pulling drag coefficient which is a
the skydiver down, drag acts in the opposite direction of motion; in this case function of Reynold’s num-
where the skydiver is falling down, drag is pushing the skydiver up. ber, and A is the reference
cross-sectional area (here
the cross-sectional area
Listing 11.12 freefall_2.m
of the skydiver). We lump
1 function res = freefall_2(t, X) these parameters into a
2 z = X(1); % the first element is position single parameter (drag con-
3 v = X(2); % the second element is velocity stant), c for convenience.
4
5 dzdt = v;
6 dvdt = acceleration(t, z, v);
7
8 res = [dzdt; dvdt]; % pack the results in a column vector
9 end
10 function res = acceleration(t, z, v)
11 a_grav = -9.8; % acceleration of gravity in m/s^2
12 c = 0.2; % drag constant
13 m = 75; % mass in kg
14 f_drag = c * v^2; % drag force in N
15 a_drag = f_drag / m; % drag acceleration in m/s^2
16 res = a_grav + a_drag; % total acceleration
17 end
398 Chapter 11 Second-order systems
The sign of the drag force (and acceleration) is positive as long as the object is
falling, the direction of the drag force is up; the drag force always acts opposite
to the direction of motion (or the velocity). The net acceleration is the sum of
gravity and drag. Be careful when you are working with forces and accelerations;
make sure you only add forces to forces or accelerations to accelerations. In my
code, I use comments to remind myself what units the values are in. That helps
me avoid nonsense like adding forces to accelerations.
Here’s what the result looks like with air resistance:
4000
3500
3000
2500
2000
1500
1000
500
-500
0 5 10 15 20 25 30
>> Z = M(:,1);
>> V = M(:,2);
>> figure
>> subplot(2,1,1) % First plot in 2 by 1 matrix
11.4 Air resistance 399
>> plot(T,Z,'-r')
>> ylabel('position [m]')
>> subplot(2,1,2) % Second plot in 2 by 1 matrix
>> plot(T,V,'-b')
>> ylabel('velocity [m/s]')
>> xlabel('time [s]')
4000
3500
position [m]
3000
2500
2000
0 5 10 15 20 25 30
-20
velocity [m/s]
-40
-60
-80
0 5 10 15 20 25 30
time [s]
Figure 11.4 Creating a stacked plot of our results with air resistance.
Lastly, let’s update freefall_c to use our updated acceleration file to solve
for the time to contact and the corresponding velocity.
400 Chapter 11 Second-order systems
We find that it takes longer to contact the ground, and the velocity at impact is
smaller. (Here it is 135.6 miles per hour.) The final velocity here corresponds to
the terminal velocity.
11.4 Air resistance 401
Example 11.1
Let’s increase the mass of the skydiver and confirm that terminal velocity increases.
This relationship is the source of the intuition that heavy objects fall faster; in
air, they do! (In vacuum, they don’t! Have you ever seen the famous Apollo 15
hammer and feather experiment?) In your solution, you can just update the mass
in acceleration. But could you also think of how to allow the user the specify
the mass in the Command Window?
Solution: Let’s double the mass from 75 kg to 150 kg. To solve, let’s update
freefall_2_contact to use the larger mass, and to also create a stacked plot of
the position and velocity versus time.
Solving:
So the heavier person takes less time to contact the ground, and has a higher
terminal velocity. And here is the resulting plot:
4000
3000
position [m]
2000
1000
0
0 10 20 30 40 50 60
-20
velocity [m/s]
-40
-60
-80
-100
0 10 20 30 40 50 60
time [s]
Figure 11.5 Creating a stacked plot of our results with air resistance where
we have doubled the mass of the object.
We again find that after a short period of time the position decreases linearly.
Now how could we allow the user to specify the mass in the Command Win-
dow? Well, the only function we can call from the Command Window is the top
function, freefall_3_contact. The mass will therefore need to be passed to
freefall_3_contact. And then to get the mass to acceleration, we can make
sure it is nested in the top function. But we can not nest just acceleration.
Since it is called by freefall, we will need to nest it too. So in the final ver-
sion of my code, I will rename the top function freefall_3m_contact, and
11.4 Air resistance 403
freefall_time, freefall, and acceleration will all be parallel with each other
(at the same level) but nested in freefall_3m_contact. Also, I add as inputs to
freefall_3m_contact the mass, m, and a string that will contain the name of the
resulting figure, fname. Also, within acceleration we delete the line where the
mass was previously assigned.
Listing 11.15 freefall_3m_contact.m
1 function [t_contact,v_contact] = freefall_3m_contact(m,fname)
2 M = []; T=[]; Z=[]; % Creating variables without assigning a value.
3 t_contact = fzero(@freefall_time,[0.1,90]);
4 V = M(:,2);
5 v_contact = V(end);
6
7 % Now create the stacked plot too. Here the final
8 % time will be the contact time.
9 figure
10 subplot(2,1,1) % First plot in 2 by 1 matrix
11 plot(T,Z,'-r')
12 ylabel('position [m]')
13 subplot(2,1,2) % Second plot in 2 by 1 matrix
14 plot(T,V,'-b')
15 ylabel('velocity [m/s]')
16 xlabel('time [s]')
17 print('-depsc',fname)
18
19 function res = freefall_time(t)
20 [T,M] = ode45(@freefall, [0, t], [4000, 0]);
21 % The first column of M is position, the second is velocity
22 Z = M(:,1);
23 res = Z(end);
24 end
25
26 function res = freefall(t, X)
27 z = X(1); % the first element is position
28 v = X(2); % the second element is velocity
29
30 dzdt = v;
31 dvdt = acceleration(t, z, v);
32
33 res = [dzdt; dvdt]; % pack the results in a column vector
34 end
35
36 function res = acceleration(t, z, v)
37 a_grav = -9.8; % acceleration of gravity in m/s^2
38 c = 0.2; % drag constant
39 f_drag = c * v^2; % drag force in N
40 a_drag = f_drag / m; % drag acceleration in m/s^2
41 res = a_grav + a_drag; % total acceleration
42 end
43
404 Chapter 11 Second-order systems
44 end
Solving:
4000
3000
position [m]
2000
1000
-1000
0 10 20 30 40 50 60 70 80
0
velocity [m/s]
-20
-40
-60
0 10 20 30 40 50 60 70 80
time [s]
4000
3000
position [m]
2000
1000
0
0 10 20 30 40 50 60
-20
velocity [m/s]
-40
-60
-80
-100
0 10 20 30 40 50 60
time [s]
11.5 Parachute!
In the previous section, we saw that the terminal velocity of a 75 kg skydiver is t Screen casts are available
about 60 m/s, which is about 130 mph. If you hit the ground at that speed, you for Professor Paluch work-
ing through the following
would almost certainly be killed. That’s where parachutes come in.
three sections: Freefall, Air
resistance, and Parachute.
They should be viewed as
Example 11.2
a series of screen casts that
Modify acceleration so that after 30 seconds of free-fall the skydiver deploys a build-off of each other.
parachute, which (almost) instantly increases the drag constant to 2.7.
What is the terminal velocity now? How long (after deployment) does it take to
reach the ground?
Solving:
So with the parachute it takes much longer to hit the ground, and the terminal
velocity is much smaller. The value of 16.5 m/s corresponds to approximately 37
miles per hour. And here is the resulting plot:
4000
3000
position [m]
2000
1000
-1000
0 20 40 60 80 100 120 140 160 180
0
velocity [m/s]
-20
-40
-60
0 20 40 60 80 100 120 140 160 180
time [s]
The plots are very cool. After a short time, we reach our terminal velocity. The
velocity is constant and the position decreases linear. Then at 30 seconds we
deploy the parachute. We get a step change in the velocity, which quickly reaches
a new terminal velocity. The position continues to decrease linearly, only the slope
is smaller because the new terminal velocity is smaller in magnitude.
This solution is good and correct, and updating our code was very straightforward.
However, given that we have a step change in the drag constant at 30 s, we could
alternatively use ode45 to solve from 0 to 30 s, then take the conditions at 30 s and
use ode45 to solve from 30 s to the final time. When we solve from 0 to 30 s we
will use a a drag constant of 0.2. Then from 30 s to the final time we will use a drag
constant of 2.7. By breaking up the integration (ode45) into these two step, we
can avoid having a step change in the drag constant within the integration range.
To facilitate switching from a drag constant of 0.2 to 2.7, I will nest the functions
freefall and acceleration within parachute_2 so that they can all see the
flag variable defined in parachute_2 to indicate which drag constant should be
used. Here I will only solve for the contact time using fzero.
408 Chapter 11 Second-order systems
In this case we have only a very small change of 0.0069 s. So we see that ode45 is
11.6 Two dimensions 409
robust and adaptive, and can perform well even when integrating step changes.
So the moral of the story is, here I would stick with using parachute_contact.
The second argument of the rate function is a vector, W, with four elements. The
first two are assigned to P, which represents position; the last two are assigned
to V, which represents velocity. P and V are vectors with elements for the x and y
components. I perform a “partial” un-packing of W because the governing ODEs in
410 Chapter 11 Second-order systems
the x and y direction will be the same. The only difference will be the acceleration
in each direction, but that will be handled in the separate acceleration function.
The result from acceleration is also a vector; ignoring air resistance (for
now), the acceleration in the x direction is 0; in the y direction it’s −g . Other than
that, this code is similar to what we saw in Section 11.3.
Note that if you wished to perform a “complete” un-packing of W you could,
and here is what that might look like:
Use what makes most sense to you, but I will build off of projectile.
If we launch the human cannonball from an initial height of 3 meters (x 0 = 0
m, y 0 = 3 m), with initial velocities of 40 m/s and 40 m/s in the x and y direction
(v x,0 = v y,0 = 40 m/s), the ode45 call would look like this:
400
350
300
250
200
150
100
50
-50
-100
0 2 4 6 8 10
44 dPdt = V;
45 dVdt = acceleration(t, P, V);
46
47 res = [dPdt; dVdt];
48 end
49
50 function res = acceleration(t, P, V)
51 g = 9.8; % acceleration of gravity in m/s^2
52 res = [0; -g];
53 end
Trajectory in xy plan
100
80
60
40
y position [m]
20
-20
-40
-60
-80
0 50 100 150 200 250 300 350 400 450
x position [m]
the correct final time to use. We see that toward the end of the trajectory the y
position is negative. This would correspond to the human cannonball tunneling
through the Earth! Next, let’s update our code so that our trajectory ends when
contact is made with Earth. This would correspond to when y = 0.
To accomplish this, I re-save projectile_model as projectile_model_2,
and update the name of the top function accordingly. I then nest within this top
function an error function named projectile_solve. Within this error function
I solve the system of ODEs over the range t 0 to t , and return the y position at t .
This will allow me to add an fzero call in the top function to solve for t when
y = 0. The last time fzero calls the error function with when we find the desired
t . By nesting, we save a final call to ode45 and it facilitates passing parameters to
the error function. Here is my updated code and solution.
31 X = M(:,1);
32 Y = M(:,2);
33 VX = M(:,3);
34 VY = M(:,4);
35
36 % Next, let's plot the trajectory in the xy plane
37 figure(1)
38 plot(X,Y,'-k')
39 xlabel('x position [m]')
40 ylabel('y position [m]')
41 title('Trajectory in xy plan')
42 print('-depsc','xy_trajectory.eps')
43
44 function res = projectile_solve(t)
45 % Solving our ODE over the range t0 to t
46 [T,M] = ode45(@projectile,[t0,t],[x0,y0,vx0,vy0]);
47
48 % Returning the y position at t
49 res = M(end,2);
50 end
51
52 end
53
54 function res = projectile(t, W)
55 P = W(1:2);
56 V = W(3:4);
57
58 dPdt = V;
59 dVdt = acceleration(t, P, V);
60
61 res = [dPdt; dVdt];
62 end
63
64 function res = acceleration(t, P, V)
65 g = 9.8; % acceleration of gravity in m/s^2
66 res = [0; -g];
67 end
Trajectory in xy plan
100
90
80
70
60
y position [m]
50
40
30
20
10
0
0 50 100 150 200 250 300 350 400
x position [m]
44 res = M(end,2);
45 end
46
47 end
48
49 function res = projectile(t, W)
50 P = W(1:2);
51 V = W(3:4);
52
53 dPdt = V;
54 dVdt = acceleration(t, P, V);
55
56 res = [dPdt; dVdt];
57 end
58
59 function res = acceleration(t, P, V)
60 g = 9.8; % acceleration of gravity in m/s^2
61 res = [0; -g];
62 end
380
370
360
Distance traveled [m]
350
340
330
320
310
30 35 40 45 50 55 60
Launch angle [degrees]
tors (which could be included in your code), you can use the built-in function
max, which returns the maximum element of a vector.
F drag = −c v 2 v̂
where v̂ is a unit vector that gives the direction of motion, so that −v̂ has the
effect of having F drag act in the opposite direction of the motion. Computing v̂
using MATLAB is straightforward using the built-in sign function.
Let’s start by updating projectile_model_2 (Listing 11.21) to include drag.
We will assume our human cannonball has the same mass as our skydiver, 75 kg.
To include the effect of drag, we need just update the acceleration function. I
will also update the code to return only the distance traveled. I will name this
updated code projectile_resistance.
420 Chapter 11 Second-order systems
Trajectory in xy plan
90
80
70
60
50
y position [m]
40
30
20
10
-10
0 50 100 150 200 250 300
x position [m]
I could keep updating the code, but it is also easy enough to solve in the Com-
mand Window.
The minimum velocity required is 24.5105 m/s; that’s about 54.8 mph. You will
11.6 Two dimensions 425
Where here only a single plot is generated with our final answer. Cool!
The minimum velocity of 54.8 mph is less than the velocity suggested by
Wikipedia of over 70 mph (approximately 31.3 m/s). Likely my estimate of c was
off. But that was a fun problem nonetheless.
>> x = 1:3
x = 1 2 3
>> y = 4:5
y = 4 5
>> z = [x, y]
z = 1 2 3 4 5
Inside brackets, the comma operator performs horizontal catenation. The vertical
catenation operator is the semi-colon. Here is an example with matrices:
>> X = zeros(2,3)
X = 0 0 0
0 0 0
>> Y = ones(2,3)
Y = 1 1 1
1 1 1
>> Z = [X; Y]
428 Chapter 11 Second-order systems
Z = 0 0 0
0 0 0
1 1 1
1 1 1
These operations only work if the matrices are the same size along the dimen-
sion where they are glued together. If not, you get:
>> a = 1:3
a = 1 2 3
>> b = a'
b = 1
2
3
>> c = [a, b]
Error using horzcat
Dimensions of matrices being concatenated are not consistent.
>> c = [a; b]
Error using vertcat
Dimensions of matrices being concatenated are not consistent.
As long as both dPdt and dVdt are column vectors, the semi-colon performs
vertical catenation, and the result is a column vector with four elements. But
11.8 ODE Events 429
if either of them is a row vector, that’s trouble. ode45 expects the result from
projectile to be a column vector, so if you are working with ode45, it is probably
a good idea to make everything a column vector.
In general, if you run into problems with horzcat and vertcat, use size to
display the dimensions of the operands, and make sure you are clear on which
way your vectors go.
1. Before calling ode45 you use odeset to create an object called options
that contains values that control how ode45 works:
In this case, the name of the option is Events and the value is a func-
tion handle. When ode45 runs, it will invoke event_function after each
timestep. You can call this function anything you want, but the name
event_function helps eliminate confusion. If you wish to define multiple
Events in a single code, then you might expand the name to prevent mixing
them up (i.e., event_function_projectile).
2. The function you provide has to take the same input variables as your rate
function. For example, here is an event function that would work with
projectile (Listing 11.18) from Section 11.6 to find the time when the
projectile hits the ground:
430 Chapter 11 Second-order systems
t Remember, you do not need t is the time, and W is a vector of the value of our functions at t. For
to unpack W at all. It is a
projectile, this is the position in the x and y direction, and the velocity
habit I tend to do to elim-
in the x and y direction, respectively. Here I unpack just the height since
inate any confusion. You
could just as well use value we don’t use the others.
= W(2). events returns three output variables:
a) value determines when an event occurs. In this case value gets the sec-
ond element of W, which is understood to be the height of the projectile.
An “event” is a point in time when this value passes through 0.
b) direction determines whether an event occurs when value is increas-
ing (direction=1), decreasing (direction=-1), or either (direction=0).
c) isterminal determines what happens when an event occurs. If isterminal=1,
the event is “terminal” and the simulation stops. If isterminal=0, the
simulation continues, but ode45 does some additional work to make
sure that the solution in the vicinity of the event is accurate, and that
one of the estimated values in the result is at the time of the event.
250
200
150
100
50
-50
0 1 2 3 4 5 6 7
If you wished to save the result to variables, the command would be:
Notice the two extra outputs. TE is the time where the event occurred, and
is a scalar for this case; if more than one event occurs, then it will be a vector.
ME is the value of the functions when the event occurred. Here it will be a
row vector of length 4, where the first element is x, second element is y,
third element if v x , and fourth element is v y :
>> TE
TE = 6.2209
>> ME
ME = 248.8347 0 40.0000 -30.9645
This is in exact agreement with our previous result using fzero. If more
than one event occurs, ME will be a matrix, where each row corresponds to
an the event at the corresponding time in TE. (Think of the relation between
T and M, only here we consider only times when events occur.)
If the use of fzero and Events give the same result, why should I consider
using Events? Remember, when using fzero we solved our ODEs for sev-
eral values of t until our root was found. The use of Events is much less
computationally demanding. While not noticeable here, it may make a
432 Chapter 11 Second-order systems
Example 11.3
How would you modify events to stop when the height of the projectile falls
through 3 m?
Solution: Similar to when using fzero, an event will be triggered when value
is equal to zero. So to find when the height is equal to 3 m, we will subtract 3
from the current value of the height. Additionally, notice that the problem asks
when the height falls through 3 m. I will take this to refer to when the projectile is
descending. We will therefor use a direction of –1.
And now to execute and find then print the height and the time to reach a height
of 3 m:
11.8 ODE Events 433
>> ME(2)
ans = 3.0000
>> TE
ans = 6.1224
Example 11.4
Multiple Events. Find the time when the height of the projectile falls through 3 m
and when it makes impact with the ground. (The integration should be stopped
upon impact.) Plot the trajectory and label when the height falls through 3 m.
Solution: Let’s start by updating our event_function_y3 from the last example.
Since we have two events, our variables will all be row vectors of length 2. For
isterminal, for the first event we will indicate to continue the integration, and
then stop at the second event.
Now to run and obtain the desired information. Since we will have two events, TE
and ME contain additional information. I will print the values and then and see if
you can interpret the results:
434 Chapter 11 Second-order systems
>> TE
TE = 6.1224
6.2209
>> ME
ME = 244.8980 3.0000 40.0000 -30.0000
248.8347 0 40.0000 -30.9645
TE is now a column vector with two rows. The first entry is the time of the first
event, the second entry is the time of the second event. For ME, we now have two
rows. The first row corresponds to the value of the functions at the first event, and
the second row corresponds to the value of the functions at the second event.
Now let’s plot the trajectory and our first event:
>> hold on
>> plot(M(:,1),M(:,2),'-r',ME(1,1),ME(1,2),'bo')
>> xlabel('x [m]')
>> ylabel('y [m]')
50
45
40
35
30
y [m]
25
20
15
10
0
0 50 100 150 200 250
x [m]
Example 11.5
Finding the maximum in the trajectory. Find the time when the height of the pro-
jectile is a maximum and when it makes impact with the ground. (The integration
should be stopped upon impact.) Plot the trajectory and label the maximum.
Solution: As compared to the last problem, here we are asked to find a maximum
in the trajectory. This will correspond to the point where d y/d t = 0, and is then
decreasing after the event. (To the right of the top of the hill we are going down.
Note if we were looking for a minimum the opposite would be the case.) For our
problem d y/d t = v y .
>> TE
TE = 3.0612
6.2209
>> ME
ME = 122.4490 48.9184 40.0000 -0.0000
248.8347 0 40.0000 -30.9645
So the maximum occurs at 3.0612 seconds, and the maximum height is 48.9184 m.
Plotting:
>> hold on
>> plot(M(:,1),M(:,2),'-r',ME(1,1),ME(1,2),'bo')
>> xlabel('x [m]')
>> ylabel('y [m]')
436 Chapter 11 Second-order systems
50
45
40
35
30
y [m] 25
20
15
10
0
0 50 100 150 200 250
x [m]
Example 11.6
Minimum and Maximum. Let’s revisit the Lotka-Voltera population model of
Section 10.1. We used this predator and pray model to model the population of
rabbits and foxes. Solving or a period of 1 year (or 365 days), we found that the
rabbit and fox population went through a series of minimums and maximums.
Find the time when the population of rabbits and foxes experiences a minimum
and maximum. Plot these points along with the population over the course of the
year.
Solution: Let’s start by writing a function for Events. Since we are looking for
minimum and maximum, we will need to find where the first derivative is zero.
How do we get the first derivative? Use our rate function! How do we identify
maximum versus minimum? For a maximum, after the maximum the function is
decreasing. For minimum, after the minimum the function is increasing.
11.8 ODE Events 437
And now solving. To distinguish our events, we now add an extra output variable
IE. IE will be a column vector of the same length of T, which indicates which event
has occurred (here 1, 2, 3, or 4) at each time. The order of the events agrees with
value, isterminal, and direction.
2
4
1
3
2
>> TE
TE =
0.0000
11.4774
27.6459
49.9308
66.1076
77.5703
93.7310
116.0370
132.2143
143.6729
159.8410
182.1584
198.3378
209.7896
225.9572
248.2904
264.4706
275.9181
292.0897
314.4344
330.6162
342.0582
358.2304
Nice! It would be great if we could isolate occurrences for each event... We can do
that too! Remember logical statements as applied to vectors.
>> IE==1
ans =
1
0
0
0
1
0
0
0
1
0
0
0
1
11.8 ODE Events 439
0
0
0
1
0
0
0
1
0
0
The result is a vectors of 1’s and 0’s. 1 correspond to true, 0 false. This tells us which
events correspond to event 1 and which do not. We can pass this information to
our vector TE to find all times for which event 1 occurs:
>> TE(IE==1)
ans =
0.0000
66.1076
132.2143
198.3378
264.4706
330.6162
The same could be done for the other events, but we will not do so here in the
interest of space. Now let us plot the results of the number of rabbits and foxes
versus time, and label the maximums and minimums. I will use a different color
symbol for the maximum and minimum of rabbits and foxes. I will start by sepa-
rating M and VE into two separate vectors, one for the population of rabbits, the
other for the population of foxes.
120
100
80
population
60
40
20
0
0 50 100 150 200 250 300 350 400
time [days]
Figure 11.17 Max and mins in our predator and prey model.
Note that in the previous two examples we knew what the order of the events
would be. I.e., the projectile would go through a maximum before hitting the
ground. But there too IE could be very useful in identifying events as part of a
larger program, and it can help reduce mistakes.
1. If a terminal event occurs during the first step of the integration, then the
solver registers the event as nonterminal and continues integrating.
2. If more than one terminal event occurs during the first step, then only the
first event registers and the solver continues integrating.
I do not expect this to cause any issues in this class, but I thought it good to
mention them. If you encounter an issue, you can look at the documentation
for additional options to increase the accuracy of the integration or decrease the
maximum step size.
11.10 Glossary 441
11.10 Glossary
parallel functions: Two or more functions defined side-by-side, so that one ends
before the next begins.
inner function: A function defined inside another function definition. The inner
function can access the variables of the outer function.
11.11 Exercises
Exercise 11.1 For our human cannonball problem, we found that the optimal launch
angle in vacuum was 45 degrees (see Listing 11.22). Let’s build-up this code. In your
solution, use fzero unless told otherwise.
1. Update your code to account for air resistance (drag). What is the optimal launch
angle with air resistance? You should find that it does not change much.
2. The acceleration due to drag takes the form a drag = F drag /m. We therefore expect
that as mass decreases, the effect of drag increases. Compute the optimal launch
angle over the range 0.04593 kg (mass of a golf ball) to 75 kg (the mass of our
human cannonball). Plot the optimal launch angle versus mass and comment on
your findings.
3. Similar to the last exercise, F drag is proportional to the velocity squared. How
does the initial velocity effect the optimal launch angle? Consider the case of a
golf ball (0.04593 kg) and our human cannonball (75 kg). Compute the optimal
launch angle over a range of velocities from 5 m/s (about 11 mph) to 50 m/s (about
112 mph). Plot the optimal launch angle versus initial velocity for each case and
comment on your findings.
4. Last, repeat your calculations using using ODE Events in place of fzero. Comment
on the improved efficiency (and maybe readability) of your code.
If you want to compare the exact execution time of the calculation using fzero
and ODE Events you can use the built-in MATLAB function timeit. Imagine my
function is called test and takes no input variables. Then to run and time from
the Command Window, I would call the function as timeit(test). It will return
the execution time in seconds. Note that it performs the calculation several times
to get an average execution time, so be patient if it is taking a little longer than
expected.
Due to the numerical precision of ODE Events, your results may differ from those
obtained using fzero. They should be close, but not exactly the same. Comment
on any differences observed. Know that it is possible to update the default settings
and improve the accuracy of the ODE Events calculations. An example would be:
442 Chapter 11 Second-order systems
In this chapter we will learn about interpolating with MATLAB. By the end of this
chapter you will be able to:
• Demonstrate use of interp1 to interpolate with respect to a single inde-
pendent variable
T contains the time values where ode45 estimated the population; Y contains the
population estimates.
443
444 Chapter 12 Interpolation
Now suppose we would like to know the population on the 180th day of the
year. We could search T for the value 180:
>> find(T==180)
ans = Empty matrix: 0-by-1
But there is no guarantee that any particular value appears in T. We can find the
index where T crosses 180:
I gets the indices of all elements of T greater than 180, so I(1) is the index of the
first one.
Then we find the corresponding value from Y:
1. We can create a function to solve the differential equation over the range 0
to t , and return the value at t . For this problem we would pass t = 180 and
the function would return the exact population at 180 days.
2. We can use ODE Events. Here we would create an event for t = 180 (or
value = t-180). In this chapter we will learn yet another technique, inter-
polation. Interpolating is a broadly applicable technique, not just limited
to differential equations.
12.2 Interpolation
t Here we will use interp1 A better option is to draw a straight line between the two points that bracket Day
for interpolating in 1-D. 180 and use the line to estimate the value in between. This process is called linear
interp2, interp3, and
interpn also exist to inter-
polate in 2-D, 3-D, and N-D,
respectfully.
12.2 Interpolation 445
interpolation, and MATLAB provides a function named interp1 that does it:
The first two arguments specify a discrete map from the values in T to the values
in Y. The third argument is the time value where we want to interpolate. The
result is what we expected, about halfway between the values that bracket it.
interp1 can also take a fourth argument that specifies what kind of interpo-
lation you want. The default is ’linear’, which does linear interpolation. Other
choices include ’spline’ which uses a spline curve to fit two points on either
side, and ’pchip’, which uses piecewise cubic Hermite interpolation. (Note
that ’pchip’ was formerly ’cubic’). In addition to the MATLAB documentation,
there is an interesting MATLAB blog page titled: “Splines and Pchips” that you
may find interesting.
t Note that if you use
>> pop = interp1(T, Y, 180, 'spline') ’cubic’ you will get a warn-
ing that the name will be
pop = 38.6486
discontinued in future re-
leases of MATLAB. MAT-
>> pop = interp1(T, Y, 180, 'pchip') LAB instead wants you to
pop = 38.6491 use the keyword ’pchip’,
which they say does the
In this case we expect the spline and cubic interpolations to be better than linear, same thing.
because they use more of the data, and we know the function isn’t linear. But we
have no reason to expect the spline to be more accurate than the cubic, or the t Note that MATLAB has
other way around. Fortunately, they are not very different. built-in functions spline
We can also use interp1 to project the rat population out beyond the values and pchip for interpolating
in T: with these specific meth-
ods. I will exclusively use
interp1 as it is more gen-
>> [T(end), Y(end)] eral an gives us greater
ans = 365.0000 76.9530 flexibility.
This process is called extrapolation. For time values near 365, extrapolation may
be reasonable, but as we go farther into the “future,” we expect them to be less
accurate. For example, here is the estimate we get by extrapolating for a whole
year:
Here we have passed the optional argument pchip for our interpolation
method, which we in turn used to extrapolate. What if we didn’t list a method so
as to use the default, and then try to extrapolate? Let’s give it a try:
What gives? If four input parameters are provided, as we did by specifying the
method, and the method is either pchip (or cubic) or spline, MATLAB will allow
you to perform extrapolation. For all other cases (i.e., linear), you will need to
pass an additional optional argument, ’extrap’, to perform an extrapolation. In
your input list, ’extrap’ would follow the method used. If you wish to extrapo-
late using linear interpolation, you will need to specify the method as ’linear’
followed by ’extrap’. If the method is pchip (or cubic) or spline, then you do
not need to list it if you do not want to, it is the default setting. Let’s try again to
extrapolate with linear interpolation:
Example 12.1
The Dortmund Data Bank (DDBST) has made the following set of isothermal va-
por/liquid equilibrium data freely availabe for the binary system acetone(1)/benzene(2)
at 318.15 K. 1
(a) Using the provided data, estimate the liquid (x 1 ) and vapor (y 1 ) mole fracs
at 55 kPa using interp1.
(b) Use interp1 to estimate values of the pressure (p) and y 1 over the range
0 ≤ x 1 ≤ 1, and plot the results.
1 http://www.ddbst.com/en/EED/VLE/VLE%20Acetone%3BBenzene.php
12.2 Interpolation 447
p (kPa) x1 y1
33.428 0.04700 0.14440
36.666 0.09630 0.25740
43.230 0.22070 0.44170
46.450 0.29360 0.52040
50.647 0.40110 0.61390
53.293 0.47590 0.66970
57.722 0.61250 0.76140
60.527 0.70450 0.82010
63.380 0.80810 0.88050
66.037 0.90840 0.94180
67.189 0.95290 0.96990
Note that while here I enter the data and perform all of the calculations in the
Command Window for illustrative purposes, a script would likely be a better idea.
We find the predictions using linear interpolation and pchip are in excellent agree-
ment. Next, let’s make a plot.
>> hold on
>> plot(Pref,Xref,'ok')
>> plot(Pref,Yref,'ok')
>> plot(Plinear,Xeval,'-r')
>> plot(Plinear,Ylinear,'-r')
>> plot(Ppchip,Xeval,'-g')
>> plot(Ppchip,Ypchip,'-g')
>> xlabel('x_1 or y_1')
>> ylabel('p [kPa]')
>> title('acetone(1)/benzene at 318.15 K')
acetone(1)/benzene at 318.15 K
1
0.9
0.8
0.7
0.6
p [kPa]
0.5
0.4
0.3
0.2
0.1
0
30 35 40 45 50 55 60 65 70
x 1 or y 1
We find that both sets of predictions are again in excellent agreement, and appear
to well represent the data. Note that the fit is not perfect as indicated by the fact
that the lines (bubble and dew) do not meet up in the limit that x 1 → 0.
This use of interp1 might be confusing if you think of the arguments as x and
y. You might find it helpful to think of them as the range and domain of a map
(where the third argument is an element of the range).
The following plot shows f (Y plotted as a function of T) and the inverse of f
(T plotted as a function of Y).
>> figure
>> subplot(2,1,1) % First plot in 2 by 1 matrix
>> plot(T, Y,'-r')
>> ylabel('Y')
>> xlabel('T')
>> subplot(2,1,2) % Second plot in 2 by 1 matrix
>> plot(Y, T, '-b')
>> ylabel('T')
>> xlabel('Y')
80
60
Y
40
20
0
0 50 100 150 200 250 300 350 400
T
400
300
200
T
100
0
0 10 20 30 40 50 60 70 80
Y
18
16
14
12
Y
10
2
0 50 100 150 200 250 300 350 400
T
So on Day 260, the population is about 15, but if we ask on what day the popula-
tion was 15, there are two possible answers, 172.44 and 260.44. If we try to use
interp1, we get the wrong answer:
On Day 196, the population is actually 16.8, so interp1 isn’t even close! The
problem is that T as a function of Y is a multivalued mapping; for some values
in the range there are more than one values in the domain. This causes interp1
to fail. The issue is interp1 expects a single valued functional relationship, and
unfortunately this is not clearly communicated in the documentation. However,
if our function is multivalued but we have an idea of where the solution occurs,
we can specify bounds (or a range) for the search. For example:
Nice! We will see in the next section how you can use fzero to obtain the same
result.
t From the documentation
for interp1, linear re-
12.4 Creating a function quires atleast 2 points, and
pchip and spline require
In your code you may find it useful or desirable to have a function that interpo- alteast 4 points.
lates within T and Y or Y and T. This could be desirable for a number of reasons,
such as: you will be interpolating often, wish to use T and Y again, will re-solve
the ODE with different conditions within your code, wish to use other built-in
functions, etc. We can do this quickly using an anonymous functions. Continuing
on from the previous section:
>> fzero(yt,260)
ans =
260.4365
-2
-4
-6
-8
-10
-12
>> yt(400)
12.5 Field mice 453
ans = 14.3354
>> yt(400)
ans = NaN
g (t , y) = a y − b(t )y 1.7
where t is time in months, y is population, a is a parameter that characterizes
population growth in the absence of limitations, and b is a function of time that
characterizes the effect of the food supply on the death rate.
Although b appears in the equation as a continuous function, we might not
know b(t ) for all t . Instead, we might only have discrete measurements:
t b (t )
0 0.0070
1 0.0036
2 0.0011
3 0.0001
4 0.0004
5 0.0013
6 0.0028
7 0.0043
8 0.0056
If we use ode45 to solve the differential equation, then we don’t get to choose the
values of t where the rate function (and therefore b) gets evaluated. We need to
provide a function that can evaluate b everywhere:
2 This example is adapted from Gerald and Wheatley, Applied Numerical Analysis, Fourth Edition,
Addison-Wesley, 1989.
454 Chapter 12 Interpolation
Example 12.2
Write a rate function that uses interpolate_b to evaluate g and then use ode45 to
compute the population of field mice from t = 0 to t = 8 with an initial population
of 100 and a = 0.9.
Then modify interpolate_b to use spline interpolation and run ode45 again to
see how much effect the interpolation has on the results.
Our rate function will contain the population growth parameter a. While we could
explicitly define it in the file, it would be nice if I could pass it as a parameter.
Passing it is not possible since ode45 will expect just two input parameters. I will
therefore write a top function that I can pass a and solve the ODE, then I will nest
the rate function within so that it can see a. Likewise, I will pass to the top function
a flag variable. When flag == 1 we will use linear interpolation for b, otherwise
we will use spline interpolation. By nesting the rate function, it can see the value of
the flag variable. It will then pass this to a modified version of interpolation_b
12.5 Field mice 455
which will perform linear or spline interpolation depending on the value of flag
passed to it.
In addition to solving and returning the result to the Command Window , I will also
plot the results within the function to help visualize whether using spline interpo-
lation makes a difference. Below are my functions followed with their use from the
Command Window and the results.
×10 4
7
linear interpolation
spline interpolation
6
5
field mice population
0
0 1 2 3 4 5 6 7 8
time [months]
Looking at the plot, there does not seem to be much difference on the result when
using linear and spline interpolation of b. From the figures, the growth appears to
be exponential, so let us plot the log of the population versus time. This should
linearize the functions. We will then see if they still appear to be the same.
12.6 Good results come from good users 457
>> plot(linear(:,1),log(linear(:,2)),'-r')
>> hold on
>> plot(spline(:,1),log(spline(:,2)),'-b')
>> xlabel('time [months]')
>> ylabel('ln(field mice population)')
>> legend('linear interpolation','spline interpolation')
12
linear interpolation
11 spline interpolation
10
ln(field mice population)
4
0 1 2 3 4 5 6 7 8
time [months]
The functions still appear to be the same, so the use of spline made little difference
as compared to using linear interpolation.
solution to our ODE was small and not noticeable. Let’s take a closer look. Let’s
plot the reference data. Then, for a time range of 0 to 8 months in increments of
0.1 months, let’s perform linear interpolation and spline interpolation. Then plot
the results and compare to our reference data.
>> T = 0:8;
>> B = [70 36 11 1 4 13 28 43 56] * 1e-4;
>> plot(T,B,'ro')
>> hold on
>> Teval = 0:0.1:8;
>> linear = interp1(T, B, Teval,'linear',NaN);
>> spline = interp1(T, B, Teval, 'spline',NaN);
>> plot(Teval,linear,'-b')
>> plot(Teval,spline,'-g')
>> xlabel('time [months]')
>> ylabel('b')
>> legend('ref data','linear','spline')
×10 -3
7
ref data
linear
spline
6
4
b
0
0 1 2 3 4 5 6 7 8
time [months]
following data:
t c (t )
0 1.0000
2 7.3891
4 54.5982
6 403.4288
This actually corresponds to c = exp(t ), which we will use to compare our results.
Let’s start by constructing a plot just as we did for b, where here we will also plot
the c = exp(t ) data as a reference.
>> T = 0:2:6;
>> C = [1.0000, 7.3891, 54.5982, 403.4288];
>> hold on
>> plot(T,C,'ro')
>> Teval = 0:0.1:6;
>> Cref = exp(Teval);
>> Linear = interp1(T, C, Teval,'linear',NaN);
>> Spline = interp1(T, C, Teval, 'spline',NaN);
>> Pchip = interp1(T, C, Teval,'pchip',NaN);
>> plot(Teval,Cref,'--r')
>> plot(Teval,Linear,'-b')
>> plot(Teval,Spline,'-g')
>> plot(Teval,Pchip,'-k')
>> xlabel('t')
>> ylabel('c')
>> legend('ref data','exp(t)','linear','spline','pchip')
460 Chapter 12 Interpolation
450
ref data
400 exp(t)
linear
spline
350 pchip
300
c 250
200
150
100
50
0
0 1 2 3 4 5 6
t
>> exp(5)
ans = 148.4132
>> interp1(T,C,5,'linear')
ans = 229.0135
>> interp1(T,C,5,'spline')
ans = 175.0107
>> interp1(T,C,5,'pchip')
ans = 176.9537
Even with spline this deviation corresponds to approximately 18%. This might be
okay for some applications, but not for others.
What are we to do? Have you ever heard of logarithmic graph paper before?
Natural log (ln) is log with base e. It received its name because ln and e appear
in many expressions when modeling physical systems. (Key point, they appear
12.6 Good results come from good users 461
a lot.) In the pre-MATLAB days there was, and still is today, a desire to plot data
such that it appears to be linear. Taking the log of an exponential function, or
a variable raised to a power, the result is a linear function. This is often called
linearization. Let us take the log of c, plot the result, and then see the result
of performing linear and spline interpolation on this new data set. I will then
un-linearize the interpolation results and plot them against the original reference
data.
t Remember in MATLAB log
>> LOGC = log(C); is ln, and log10 is log.
>> plot(T,LOGC,'ro')
>> hold on
>> RefLOG = Teval;
>> LinearLOG = interp1(T, LOGC, Teval,'linear',NaN);
>> SplineLOG = interp1(T, LOGC, Teval, 'spline',NaN);
>> PchipLOG = interp1(T, LOGC, Teval, 'pchip',NaN);
>> plot(Teval,RefLOG,'--r')
>> plot(Teval,LinearLOG,'-b')
>> plot(Teval,SplineLOG,'-g')
>> plot(Teval,PchipLOG,'-k')
>> xlabel('t')
>> ylabel('ln c')
>> legend('ref data','x','linear','spline','pchip')
7
ref data
x
6
linear
spline
pchip
5
4
ln c
0
0 1 2 3 4 5 6
t
Beautiful! All three methods result in a perfect match. Let’s write an anonymous
function to perform linear interpolation of ln c versus t , and then to return the
value of c, which is what we actually want. We can then calculate the value at
t = 5 to compare to our previous result. In writing the function, I will assume we
just have our original data tabulated as T and C.
In perfect agreement with the reference curve. This is a huge improvement over
performing interpolation of c versus t , and it required minimal extra work. And
that is what I mean by “good results come from good users.”
where the temperature is in K and the pressure is in bar. Plot the data, and compare
the use of linear and spline interpolation. (Follow the work in Section 12.6.) How
do the two methods perform? Use them both to estimate the vapor pressure at 390
K.
From your thermodynamics class, you know that the Clausius-Clapeyron equation
suggests that ln p sat scales linearly with T −1 . Transform the data as suggested by
the Clausius-Clapeyron equation and re-plot it, and again compare the use of
linear and spline interpolation. Use them both to estimate the vapor pressure at
390 K.
How do the results compare?
12.6 Good results come from good users 463
Solution:
Let’s begin by entering the data in to two vectors, T_K and Psat_bar. The temper-
ature data is evenly spaced, so I will use the extended colon operator.
t Psat_bar is long, so I will
>> T_K = 300:20:500; break it up onto two lines.
>> Psat_bar = [0.1381,0.32029,0.66128,1.2425,2.1614,3.5284,5.4649,... To continue onto another
8.1007,11.575,16.037,21.651]; line, we need to add ... to
the end of the line. Then to
get to the next line in the
Next, to graphically compare the use of linear and spline interpolation, let’s use Command Window, use
linear and spline interpolation to estimate the vapor pressure over the range 300 Shift + Enter. Know that
to 500 K in increments of 1 K. To do this, we will create a new vector T_eval which if I were to omit the ...,
contains the temperatures we wish to estimate the vapor pressure at. We can then MATLAB would interpret the
use interp1 with either linear or spline interpolation, and store the results to two second line as the second
new vectors. We will then plot the original data and our interpolation results, and column in matrix.
compare the two. Remember, with linear and spline interpolation, we are only
estimating the data in between points.
25
reference
linear
spline
20
15
Psat [bar]
10
0
300 350 400 450 500
T [K]
Figure 12.10 Comparing our original reference data to the result using
linear and spline interpolation.
As far as we can tell from the figure, linear and spline interpolation appear to be
in close agreement, and appear to represent our data well. Next, let’s use both
methods to estimate the vapor pressure at 390 K.
Psat_390_linear =
2.8449
Psat_390_spline =
2.7815
>> plot(Tinv,Ln_Psat,'kx')
>> hold on
>> plot(Teval_inv,Ln_Psat_linear,'-r')
>> plot(Teval_inv,Ln_Psat_spline,'--b')
>> xlabel('1/T [1/K]')
>> ylabel('ln Psat/bar')
>> legend('reference','linear','spline')
4
reference
linear
3 spline
2
ln Psat/bar
-1
-2
2 2.2 2.4 2.6 2.8 3 3.2 3.4
1/T [1/K] ×10 -3
Figure 12.11 Comparing our original reference data to the result using
linear and spline interpolation. Here we use ln p sat versus 1/T to linearize
our data.
Again, as far as we can tell from the figure, linear and spline interpolation appear
to be in close agreement, and appear to represent our data well. As compared to
before, our transformed data now appears to be linear. While linear and spline
were in close agreement before, I suspect it is just because of the spacing used; over
the small range between data points, linear interpolation was a fair approximation.
Here, we expect linear to work well because the data appears to be linear. Let’s
see by using both linear and spline interpolation to estimate the vapor pressure at
390 K. We will interpolate using our linearized data, then un-linearize to get the
desired result.
ln_Psat_390_linear =
1.0221
Psat_390_linear_clap =
2.7790
ln_Psat_390_spline =
1.0230
Psat_390_spline_clap =
2.7815
Using spline interpolation, we find that we obtain the same result whether we
interpolate using the original data or the linearized data. Using linear interpolation,
using our linearized date the result in now in closer agreement with the result using
spline interpolation.
Figure 12.14 The reference states used by the NIST WebBook Thermophys-
ical Properties of Fluid for water, along with the critical temperature (Tc ),
critical pressure (p c ), and critical density (ρ c ), accentric factor (ω), normal
boiling point (Tb ), and the dipole moment.
12.7.2 dlmread
t As a starting point to learn Now that we have tabulated data, we need to read it into MATLAB. There are
more about importing data several ways to read-in data in MATLAB. Here we will use dlmread which is well
into MATLAB, have a look
suited for this case. The “dlm” stands for a “deliminated” text file. A deliminated
at the documentation page
“Ways to Import Text Files”.
text file is a file that contains columns of data separated by a fixed character
(typically a comma) or space (or tab). We will first go over the syntax of dlmread,
one required argument and three optional arguments, and then we will look at
the specific example of the saturated steam tables. If you would like more, have a
look at the MATLAB documentation. Know that a limitation of dlmread is that it
can only read in numeric data. There are ways to read mixed data types, but they
are more involved and will be reserved for later.
The base use would be M = dlmread(filename). filename is the name of
the file you wish to read-in and is a string. For our case this will be ‘water_sat_nist.dat’.
Note that the quotes are important to indicate that it is a string. dlmread reads in
the contents of the file and stores it in matrix M. Remember a deliminated text file
is a file that contains columns of data. Each column of the text file will be stored
to a column in M, in the order that they appear.
The first optional argument is delimiter, resulting in M = dlmread(filename,delimiter).
delimiter allows you to specify what is separating the columns in the text file.
In the previous case, when we did not specify delimiter, MATLAB detects the
12.7 Fun with tabulated pure component VLE data (i.e., the saturated steam tables) 469
delimiter from the file and treats repeated white spaces as a single delimiter.
delimiter is also a string. To specify the delimiter as a space, comma, and tab,
use ’ ’, ’,’, and ’\t’, respectively. Again, the quotes are important to indicate
that it is a string. When we downloaded the saturated steam tables, the website
indicated that it was a tab-deliminated text file, so we will use ’\t’. Note that if
you would like to stick with the default of MATLAB determining the delimiter, you
can use quotes with no space in between, “; this is also referred to as an empty
character.
The second optional argument is the the row, R1, and column, C1, offset,
resulting in M = dlmread(filename,delimiter,R1,C1). If you would like to
read-in all of the data, then R1=0 and C1=0. Recall that dlmread can only be used
to read in numeric data. In our case, the first row will be a string indicating the
property in each column. We will therefore need to skip (not read-in) the first row.
This is accomplished with R1=1 and C1=0. This means use a row offset of 1, i.e.
skip the first row, but don’t skip any columns.
The fourth optional argument is to specify the row and column range as
[R1 C1 R2 C2], resulting in M = dlmread(filename,delimiter,[R1 C1 R2
C2]). Here R1, R2, C1, and C2 are still row and column offsets. So if we wanted
to read in rows 1 to 6 of columns 1 and 2, we would have R1=0, R2=5, and C1=0,
C2=1. So if you would like to think in terms of row and column numbers rather
than row and column offsets, you need just recognize they differ by 1.
So finally, let’s read in the steam tables:
t If your text file was in a
>> M=dlmread('water_sat_nist.dat','\t',1,0); different directory, you
could still load it, you would
just need to augment its
I suppressed the output because the saturated steam tables contain a large name to provide its loca-
amount of data. If we would like to check the size of M, we can: tion. If the file were in my
home directory, located
>> whos M at “/home/paluchas”, we
Name Size Bytes Class Attributes could replace the file name
M 37x25 7400 double with “/home/paluchas/wa-
ter_sat_nist.dat”. You can
always use global com-
So M is a matrix with 37 rows and 25 columns. What does each row correspond to? mands like this. Local com-
You could find out by viewing “water_sat_nist.dat” in your favorite text editor. To mands are also possible if
view the file within the MATLAB Command Window, you can use the command you know where the file is
type ’water_sat_nist.dat’. I do not do so here in the text because the file in relation to the current
is very large. You might find that the columns do not appear to be exactly lined directory.
up. That is okay. In each row the data are separated by a tab, indicating the next
column. Where the columns do not appear to line up is due to differences in the
number of digits in a number or the use of scientific notation. You will find that
the columns correspond to the following:
470 Chapter 12 Interpolation
1. Temperature (K)
2. Pressure (bar)
3. Density (l, mol/l). Here the first “l” is used to indicate the phase as liquid.
The second “l” corresponds to liters, which should actually be capitalized,
“L”.
8. Cv (l, J/mol*K)
9. Cp (l, J/mol*K)
15. Density (v, mol/l). Now here “v” stands for vapor phase.
That is a lot of data! If I look at typical saturated steam tables in the appendix of a
thermodynamics text, they typically contain just temperature, pressure, density,
internal energy, enthalpy, and entropy. Let us therefore create a new matrix,
water_sat that contains just this data as columns:
1. Temperature (K)
2. Pressure (bar)
To accomplish this, we see we will need all rows and columns 1–3, 5–7, 15, and
17–19. This is accomplished as:
You may find that you would like to look at many of the systems provided in the
NIST WebBook Thermophysical Properties of Fluid Systems database, and this
might seem like a lot to do each time (and to remember!). We can therefore create
a function to generalize and automate the process:
472 Chapter 12 Interpolation
Nice!
12.7.3 interp1
Now let’s interpolate! Here we are dealing with a pure component system (N = 1)
at vapor-liquid equilibrium (π = 2). According to Gibbs phase rule, we have
F = N −π+2 = 1−2+2 = 1, a single degree of freedom. That is, we need only spec-
ify a single, intensive, thermodynamics function to pin down the thermodynamic
state of our system. What does that mean? You can think of it as every thermo-
dynamic property is a function of only a single variable. This will correspond to
interpolating in 1-D using interp1.
Remember, our data is tabulated from 280 to 640 K in increments of 10 K.
What if we needed the properties of saturated water at 298.15 K? Are we out of
luck? No, we can use interp1. Remember temperature is column 1 of water_sat.
Using spline interpolation:
sol =
1.0e+03 *
Columns 1 through 6
Columns 7 through 10
We could go further and use different units if MPa is not appropriate at this
temperature. Instead of MPa, we could use kPa by multiplying by a factor of 1,000:
Or you could just interpolate in pressure if that is what you are interested
in. What is important to remember is that while MATLAB may display zero, the
pressure and vapor density are not actually zero. It is just a limit of the number of
decimal places displayed. Or you could just tell MATLAB to display more decimal
places:
sol =
1.0e+03 *
Columns 1 through 3
Columns 4 through 6
Columns 7 through 9
Column 10
0.008556666952043
sol =
Columns 1 through 4
Columns 5 through 8
Columns 9 through 10
2.5465e+03 8.5567e+00
Besides temperature, you can interpolate in any variable you wish. For exam-
ple, what are the properties of our two phase system at a pressure of 0.3 MPa?
Pressure is the second column of water_sat:
sol =
Columns 1 through 4
Columns 5 through 8
Columns 9 through 10
2.7249e+03 6.9915e+00
Or how about when the liquid density is 0.8 g/mL? The liquid density is the third
column of water_sat:
476 Chapter 12 Interpolation
sol =
Columns 1 through 4
Columns 5 through 8
Columns 9 through 10
2.8011e+03 6.0773e+00
>> hold on
>> plot(water_sat(:,3),water_sat(:,1),'-ro')
>> plot(water_sat(:,7),water_sat(:,1),'-bo')
>> xlabel('Density [g/mL]')
>> ylabel('Temperature [K]')
12.7 Fun with tabulated pure component VLE data (i.e., the saturated steam tables) 477
650
600
550
Temperature [K]
500
450
400
350
300
250
0 0.2 0.4 0.6 0.8 1
Density [g/mL]
sol =
Columns 1 through 4
Columns 5 through 8
Columns 9 through 10
2.0949e+03 4.3552e+00
>> plot(sol(3),sol(1),'kx')
700
650
600
550
Temperature [K]
500
450
400
350
300
250
0 0.2 0.4 0.6 0.8 1
Density [g/mL]
h L versus T and hV versus T . We get a phase envelope very similar to the ρT case.
However, here on the right we have vapor and on the left we have liquid. (The
internal energy of a vapor will be greater than a liquid.)
>> hold on
>> plot(water_sat(:,5),water_sat(:,1),'-ro')
>> plot(water_sat(:,9),water_sat(:,1),'-bo')
>> xlabel('Enthalpy [kJ/kg]')
>> ylabel('Temperature [K]')
650
600
550
500
Temperature [K]
450
400
350
300
250
0 500 1000 1500 2000 2500 3000
Enthalpy [kJ/kg]
650
600
550
500
Temperature [K]
450
400
350
300
250
0 500 1000 1500 2000 2500 3000
Enthalpy [kJ/kg]
12.8 Glossary
interpolation: Estimating the value of a function using known values on either
side.
extrapolation: Estimating the value of a function using known values that don’t
bracket the desired value.
multivalued mapping: A mapping where at least one value in the range maps to
more than one value in the domain.
12.9 Exercises 481
12.9 Exercises
Exercise 12.1 Thermistors are used to measure the temperature of bodies. Thermistors
are based on materials’ change in resistance with temperature. To measure temperature,
manufacturers provide you with a temperature versus resistance calibration curve. If
you measure resistance, you can find the temperature. A manufacturer of thermistors
makes several observations with a thermistor, which are given at the end of the problem.
Determine the temperature corresponding to 754.8 ohms.
R (ohm) T (◦ C)
1101.0 25.113
911.3 30.131
636.0 40.120
451.1 50.128
Exercise 12.2 To try and estimate the critical point of water, we extrapolated to estimate
where the difference between the liquid and vapor density were zero. We could have
done the same thing with the enthalpy of vaporization, but given the shape of the phase-
envelope, we would expect similar results. Another approach pioneered by Guggenheim
(a really, really smart thermodynamicist) was to plot the surface tension versus the
temperature. The surface tension is the interfacial (vapor/liquid) free energy per unit
area. At the critical point the free energy barrier between the phases goes to zero, so the
surface tension goes to zero.
Try Guggenheim’s method. Plot surface tension versus temperature, then estimate
the critical point by extrapolating to where the surface tension is zero. Based on the plot,
do you expect the result to be more promising? To help you import the necessary data,
you can use the updated function import_sat_vle_gug.
As an aside, Guggenheim’s method is really a very powerful tool, that is often unap-
preciated. It’s greatest utility is estimating the critical point for non-volatile fluids; I have
seen the critical point of NaCl estimated in this way. Now why would someone want an
estimate of the critical point of NaCl? So that we can model its phase behavior using an
equation of state and to use or develop corresponding states theories.
Chapter 13
Numerical Integration
If you work through the chapter and believe these goals are not met, please
re-review the material and reach out for help.
Some of the material on numerical integration is adopted from Rachel Duke
and Spencer Sabatino from this course during Spring 2020.
13.1 Integration
Integration is a fundamental operation of Calculus and is important for the so-
lution of a wide range of engineering problems. Considering a function with a
single variable, the definite integral of the function corresponds to the signed area
of the function in the plane between two points (i.e., the “area under the curve”).
For some function, the definite integral may readily be computed analytically. For
example:
1 3 ¯¯2 1 3 1 3
Z 2 · ¸¯
2
x dx = x ¯ = 2 − 0 = 8/3 = 2.6667
0 3 0 3 3
The “Power Rule” is one I remember. Within this expression, recall x 3 /3 is the
antiderivative of x 2 . Others functions may be more complicated. However, many
483
484 Chapter 13 Numerical Integration
antiderivates may be found in a list of integrals. But some may elude us. Consider
the following:
Z 2
2
e −x d x
0
which can not be evaluated analytically nor expressed in elementary functions.
This function can, however, be evaluated numerically.
Numerical integration is the practice of applying various algorithms and ap-
proximations to solve definite integrals. We often desire to employ numerical
techniques for integration for a few reasons. First, as shown in expression sec-
tion 13.1, our integral expression might be impossible to evaluate analytically, or
at least without elementary functions, preventing us from solving by hand. An-
other big reason is simply because it would be quicker for us to evaluate integrals
indirectly using these methods without a need to consult a list of integrals. This is
similar to the motivation for solving ordinary differential equations numerically
with ode45. For example, while section 13.1 could readily be solved analytically,
to give you a preview of what’s to come, the numerical solution with MATLAB is
as easy as:
>> fune = @(X) X.^(2);
>> sol = integral(fune,0,2)
sol = 2.6667
Essentially, all that you need is a function for your function of interest.
Recall the formal definition of an integral:
Z b n
f x i∗ ∆x
X ¡ ¢
f (x) = lim
a n→∞
i =1
While this definition may look involved, all it ultimately is saying is that a definite
integral is an infinite summation of the area of infinitesimally small rectangles of
width ∆x and height f (x ∗ ). This definition ultimately forms the basis of several
common numerical integration techniques as we’ll discuss next.
these approximations often work well. The orientation of the rectangle is often
important too, as you could orient the rectangles so that the left side, right side, or
midpoint of the rectangle is on the curve, often denoted as left hand, right hand,
or midpoint sums.
8
0
0 0.5 1 1.5 2
Figure 13.1 The left-hand sum of x 3 over the range 0 to 2. Image from
Wikipedia.
486 Chapter 13 Numerical Integration
0
0 0.5 1 1.5 2
Figure 13.2 The right-hand sum of x 3 over the range 0 to 2. Image from
Wikipedia.
0
0 0.5 1 1.5 2
Figure 13.3 The midpoint sum of x 3 over the range 0 to 2. Image from
Wikipedia.
13.2 Numerical Integration 487
We see that the computed integral wil be sensitive to how the rectangles are con-
structures. Additionally, as ∆x decrases we know that the accuracy will increase.
That’s not the end of it though; why should we be limited to rectangles?
Why not try more curved and unique shapes to better match the curve we’re
working with? That’s where we run into the trapezoidal method, which relies
upon summing the area of finitely many trapezoids instead of rectangles. Similar
to our discussion of Euler’s method, let us assume that the function is linear over
the range ∆x. The integral over this range is equivalent to the area of a trapezoid,
1
2 b (h 1 + h 2 ). Here, b = x 0 + ∆x − x 0 = ∆x, h 1 = f (x 0 ) and h 2 = f (x 0 + ∆x). The
smaller the value of ∆x, the better the linear approximation, and in the limit that
∆x → 0 we expect the relation to be exact.
8
0
0 0.5 1 1.5 2
Figure 13.4 Application of the trapezoid method for x 3 over the range 0 to 2.
Image from Wikipedia.
Let’s us view the trapezoid method from a different angle. Namely, earlier I
mentioned that power rule was one that I actually remembered. That is, I could
readily compute the integral of a polynomical analytically. The trapezoid method
assumes that the funciton is linear over the range x 0 to x 0 + ∆x. We can readily
determine the equation of the line over this range as g (x) = mx + b where:
f (x 0 + ∆x) − f (x 0 )
m=
∆x
and
f (x 0 + ∆x) − f (x 0 )
b = f (x 0 ) − x 0
∆x
488 Chapter 13 Numerical Integration
where here g (x) corresponds to the linear equation used to approximate the
function f (x). Now integrating over the range x 0 to x 0 + ∆x:
Z x0 +∆x hm i¯ 1
x 2 + bx ¯ 0x0 +∆x = ∆x f (x 0 + ∆x) − f (x 0 )
¡ ¢
(mx + b) d x =
¯
x0 2 x 2
Why limit ourselves to assuming the function is linear? We could readily fit
any order polynomial which could be integrated analytically. We would expect
that higher order polynomials could better represent the function of interest over
a range ∆x, allowing for a more accurate estimate of the integral.
Additionally, I point out that ∆x need not be constant when numerically in-
tegrating. There may be some regions where smaller or larger values of ∆x are
needed to achieve a given level of accuracy. A similar observation was made
with Euler’s method. Fortunately for us, MATLAB provides an optimized func-
tion to perform numerical integration, integral which can determine the most
appropriate method and optimal ∆x.
13.2.1 integral
Evaluating definite integrals in MATLAB is rather straightforward. And if you have
made it this far in the course, I hope you find it easy, especially compared to your
Calculus course. Essentially, all that is needed is a function for the function of
interest (with a single independent variable) that you wish to integrate. Just as
with fzero, fsolve, and ode45, the inputs to this function are limited. Namely
here the function can only have a single input, the independent variable. Pa-
rameters will need to be passed via the use of anonymous functions or nested
functions. We then only need to specify the range of interest. Let’s give it a try and
evaluate the integral of a function whose antiderivative cannot be expressed in
closed form: Z 10
p −x
xe d x
0
We see our first step was to create a function. This could be in a separate function
file, or here I use an anonymous function. When using integral, your function
needs to be vectorized. Next, we use integral. In its basic form, the first variable
is the function handle for the function to integrate, the second variable is the
lower-limit, and the third argument is the upper-limit. Remember, an anonymous
function is alread of type function handle, so @ is not needed. If you have used a
separate function file, then we would need @. That’s it!
The integral function is fairly robust, and can even handle infinite limits.
Consider: Z ∞
p −x 1p
xe d x = π
0 2
13.2 Numerical Integration 489
Nice! Next, let’s have some fun! Let’s create an anonymous function to evaluate
the integral for an arbitrary upper-limit.
>> int_fun = @(xlim) integral(fun,0,xlim);
>> sol = int_fun(10)
sol = 0.8861
This is a function with a single input and single output. We can use fplot
to plot the intgral as a function of the upper-limit. Note that here we can use a
lower-limit of 0 since the integral from 0 to 0 is just... 0. This will not result in an
error as we had with ode45.
>> fplot(int_fun,[0,10])
0.8
0.7
0.6
0.5
0.4
0.3
0.2
0.1
0
0 2 4 6 8 10
Awesome!
Nice! Just to get an idea of how linear interpolation and cubic interpolation
are able to model the tabulated data, let’s make a plot:
>> hold on
>> plot(X,Y,'ko')
>> fplot(fun_linear,[0,7],'-k')
>> fplot(fun_cubic,[0,7],'-r')
13.2 Numerical Integration 491
11
10
5
0 1 2 3 4 5 6 7
Figure 13.6 The black circles are the tabulated data, the black line is the use
of linear interpolation, and the red line is the use of cubic interpolation.
Very cool. And just think, you could even combine this with fzero!
While this is relatively straightforward, know too that MATLAB has a built-in
function trapz which can be used to directly integrate tabulated data. And as
you might guess from the name, it use trapezoid method.
>> sol = trapz(X,Y)
sol = 52.8000
And as we would expect, this answer is exactly the same as the use of linear
interpolation. Does that make sense to you?
And that’s it. I trust that you are now prepared to take on any integrals in the
future, no matter how complicated. If you ever encounter multi-dimensional in-
tegrals, no worries. MATLAB still has you covered. Have a look at integral2 and
hrefhttps://www.mathworks.com/help/matlab/ref/integral3.htmlintegral3.
492 Chapter 13 Numerical Integration
13.3 Exercises
Exercise 13.1 The change in molar entropy of an ideal gas is given by the following
equation:
Z T2 ig
ig ig CP P2
∆S ig = S 2 (T2 , P 2 ) − S 1 (T1 , P 1 ) = d T − R ln
T1 T P1
where T1 and T2 are the initial and final temperature, respectively, P 1 and P 2 are the
initial and final pressure, respectively, R is the molar gas constant, 8.314 J/(mol K), and
ig
C P is the constant pressure heat capacity which is given by the following equation:
ig
CP
= a0 + a1 T + a2 T 2 + a3 T 3 + a4 T 4
R
ig
Where T is in units of K. Know that C P /R is dimensionless; multiply by R in your favorite
ig
units to get C P . For methane, we have a 0 = 4.568, a 1 = −8.975 × 10−3 , a 2 = 3.631 × 10−5 ,
a 3 = −3.407 × 10−8 and a 4 = 1.091 × 10−11 .
In this chapter we will briefly discuss the topic of Monte Carlo sampling (or in-
tegration). Monte Carlo integration is a powerful tool to calculate integrals of
complicated, multidimensional functions. What do I mean by complicated and
multidimensional? My PhD research was in the area of statistical thermodynam-
ics, and a major component of my PhD dissertation was developing strategies to
compute classical configurational integrals of fluid-phase systems. Imagine we
have a fluid-phase system consisting N Argon atoms:
Z
Z N = e −βU (~r ) d~
rN
V
where Z N is the classical configurational integral, U is the interaction energy
of the system which is a function of the location of every atom in the system,
β−1 = k B T , and the integral is over the coordinates of all of the atoms in the system.
What does this mean? The location of an atom, regardless of your coordinate
system, requires three coordinates, say x, y, and z. So if we have N atoms, this
corresponds to 3N independent variables. If we have just N = 10 atoms, this
corresponds to a function with 59,049 independent variables. These are the types
of problems that Monte Carlo integration is perfectly suited for. If you have a
simple one-dimensional (1-D) problem, stick to Simpson’s rule and the other
techniques you learned in you Calculus class. (If Simpson’s rule isn’t familiar,
what about the trapezoid method?)
So how does it work? Monte Carlo integration get’s it name from the gambling
city in Monaco, and is based on the concept of random sampling. It was developed
shortly after the time of World War II. At that time, the first supercomputers were
developed to help the war effort, and after the war, scientists were left with
some cool toys to experiment with. A group of scientists at Los Alamos National
Laboratory developed Monte Carlo integration as a tool to compute the classical
configurational integral of a hard sphere fluid. (Although the same method was
developed shortly before this time at Berkeley National Lab/UC Berkeley, but the
findings were never published.) The basic idea, as presented to me, was imagine I
493
494 Chapter 14 Monte Carlo
would like to compute the area of a pond. I have no way of measuring the area
of the pond directly. However, I can layout a rectangular bounding box around
the pond of known dimensions. I like rectangles because I can easily compute it’s
area. So this could be taken as first guess of the area of the pond, which is smaller.
The experiment then proceeds that I walk around the bounding box and
randomly throw rocks. If I hear a splash, the rock landed in the pond, it was a hit.
If I don’t hear a splash, then I hit land. I can then determine the fraction of the
rocks I randomly threw that hit the pond relative to the total number. Assuming I
randomly threw the rocks (and I threw an infinite number), this ratio should be
equal to the ratio of the area of the pond to the bounding box (the total area). So
if I multiply this number by the area of the bounding box, which is known, I get
an estimate of the area of the pond. Cool!
So let’s see it in practice and use it to numerically estimate a value of π. We
wish to compute the area of a circle with radius r = 1. The area of this circle is
A circle = πr 2 = π. However, π is unknown and is the quantity we wish to compute.
We will do so using Monte Carlo integration. To accomplish this, I center my
circle at the origin (0, 0), and then construct a bounding box with an edge length
of 2r . The area of this bounding box will be A = (2r )2 = 4. Let’s graph the circle
and bounding box:
>> hold on
>> Xs = [-1,1,1,-1,-1];
>> Ys = [1,1,-1,-1,1];
>> plot(Xs,Ys,'-k') % Plotting the bounding box
>> theta = 0:pi/100:2*pi;
>> Xc = cos(theta);
>> Yc = sin(theta);
>> plot(Xc,Yc,'-r') % Plotting the circle using 100 points
14.1 Random number generator basics 495
0.8
0.6
0.4
0.2
-0.2
-0.4
-0.6
-0.8
-1
-1 -0.5 0 0.5 1
x2 + y 2 = 1
So, if x 2 + y 2 ≤ 1, we are inside the circle. We then determine the fractions (or
density, ρ) of hits relative to the total number of trials:
Nhits
ρ=
Ntrials
Then
A circle = ρ A = 4ρ = π
Great! Now let’s do it. Before doing so, let’s pause for a moment and briefly
discuss the generation of random numbers, which is the heart of any Monte Carlo
program.
>> rand(1,n)
496 Chapter 14 Monte Carlo
>> 100*rand(1,n)
>> round(100*rand(1,n))
>> round(100*rand(1,n)-50)
will give a vector of integers randomly distributed between –50 and +50. (Note
that both the 100 and –50 could be included inside or outside the round function
with the same result.) So we can readily scale and shift our range. For our problem
of calculating π, we need random numbers between –1 and 1. This is therefore
accomplished as:
>> 2*rand(1,n)-1
14.2 Back to π
Now that we know how to generate random numbers, let’s write a function to
compute π. You will notice that I do not write a for loop to loop over all of the
random samples. When the number of samples becomes large this becomes very
slow. I instead take advantage of MATLAB’s vector operations.
Now let’s give it a try using 10, 1,000, 10,000, and 100,000 samples.
>> circle_area(10)
ans = 2
>> circle_area(1000)
498 Chapter 14 Monte Carlo
ans = 3.2160
>> circle_area(10000)
ans = 3.1332
>> circle_area(1000000)
ans = 3.1427
>> pi
ans = 3.1416
3.5
3.4
3.3
3.2
estimate of pi
3.1
2.9
2.8
2.7
2.6
0 2 4 6 8 10
number of trials 10 4
Figure 14.2 The value of π estimated using Monte Carlo integration versus
the number of samples.
We see that we converge rather quickly, but fluctuations about the mean persist.
Last, before moving on, I will update the function circle_area to plot the tri-
als (or sample points). The updated function is below, followed by plots generated
by executing the function for the case of 10, 1,000, and 10,000 trials.
500 Chapter 14 Monte Carlo
0.8
0.6
0.4
0.2
-0.2
-0.4
-0.6
-0.8
-1
-1 -0.5 0 0.5 1
0.8
0.6
0.4
0.2
-0.2
-0.4
-0.6
-0.8
-1
-1 -0.5 0 0.5 1
0.8
0.6
0.4
0.2
-0.2
-0.4
-0.6
-0.8
-1
-1 -0.5 0 0.5 1
Let’s consider the example of evaluating the integral of the function x 2 using
Monte Carlo integration over an arbitrary range, say a to b:
Z b
I= x 2d x
a
We need to begin by defining a bounding box of known area. For the case of x 2 , I
know that the function will be a maximum at either of the end points of the range
of x, a or b. So we begin by evaluating the function at the end points, finding the
maximum (y max ), and then define a bounding box of width b − a and height y max .
The area of the bounding box is A = y max (b − a).
¡ ¢
Next, we will generate our random trials of x, y coordinates such that they
fall inside our bounding box. For this case, we will generate values of x over the
range a ≤ x ≤ b and values of y over the range 0 ≤ y ≤ y max . To determine if we
have a hit, we evaluate our function at each of our trial values of x, here x 2 , and
check to see if this value is less than or equal to our corresponding trial value of y
14.3 Evaluating Integrals in General 503
One million samples takes very little time to execute, and the Monte Carlo esti-
mate differs from the reference value by just 0.07 or 0.09%.
p (x) = C
where p (x) is the probability of observing a value of x, and C is a constant. If the
probability distribution is normalized, then:
Z b Z b
p (x) d x = C d x = C (b − a) = 1
a a
For this to be true, it must be that:
1
p (x) =
b−a
Okay, so how does this help us out. The key is that b − a is a constant. So we
can re-write our original integral as:
14.4 Simplifying and Generalizing the Algorithm 505
Z b
I= f (x) d x
a
Z b
(b − a)
= f (x) dx
a (b − a)
Z b
1
= (b − a) f (x) dx
a (b − a)
Z b
= (b − a) f (x) p (x) d x
a
The integral in the last line is our definition of the expectation value of f (x).
Assuming a large number of trials (or samples, n), the expectation value may be
estimated as the arithmetic average of function:
Z b n
® (b − a) X
I= f (x) d x = (b − a) f (x) ≈ f (x i )
a n i =1
The larger the number of trials, n, the better the approximation. This results in a
much simpler algorithm:
2. Evaluate the function f (x) for each value of x from the first step.
3. Compute the arithmetic average of the function f (x) evaluated at each trial
of x.
This results in a much simpler function that may readily be generalized. Let’s
apply this new algorithm to again integrate x 2 .
506 Chapter 14 Monte Carlo
This agrees with our previous result. You must appreciate the beauty of this.
With just three lines of code, we can estimate the integral of any function (with a
single independent variable), no matter how complicated. While the advantage
of using Monte Carlo integration over conventional methods may not be clear for
functions with a single variable, the method really shines for multi-dimensional
integrals.
Before wrapping-up, let’s us consider a well known function that is known to
be both positive and negative. Let’s consider:
Z 2π
sin (x) d x = 0
0
where I have also split the integral to show the ability to evaluate the integral of
both positive and negative functions.
14.5 Exercises
Exercise 14.1 Write a script or function to demonstrate the use of monte_carlo_int.m
for a function of your choice, and compare to built-in function integral.
Chapter 15
An Introduction to Symbolic
Calculations
With our symbolic variable, we can do things like create functions. Consider the
case of:
f (x) = 4x 4 − 2x + 3
>> f = 4*x^(4)-2*x+3
f = 4*x^4 - 2*x + 3
Here we have assigned the expression on the right hand side to the variable f.
And since x is a symbolic variable, f will be symbolic too. If you would like to
evaluate your function for a specific value of x, this can be accomplishd using
the subs function. The basic call to subs used here will use as inputs: (1) the
expression, (2) the symbolic variable that you would like to substitute a numerical
value for, and (3) the numerical value you would like to substitute. For example:
f (x = 3) = 4 · 34 − 2 · 3 + 3 = 321
Nice! And not to be outdone, we can plot our function as we did before using
fplot.
>> fplot(f,[-1,1],'-k')
509
510 Chapter 15 An Introduction to Symbolic Calculations
-1 -0.5 0 0.5 1
>> g = exp(x)/x
g = exp(x)/x
Next, let’s see what happens if we try to evaluate our function again at x = 3.
>> g_3 = subs(g,3)
g_3 = exp(3)/3
Here we obtain the exact answer expressed symbolically. But what if we wanted
a numerical value? This is readily accomplished with the function double. The
function double converts a symbolic variable to a (double precision) numerical
value. Notice in the workspace window that g_3 and f_3 are of type symbolic. We
can get a numerical value as:
>> g_3_numeric = double(g_3)
g_3_numeric = 6.6952
15.1 Some Symbolic Variable Basics 511
Please consult the documentation page for assume. This can be very useful if you
are only interested in a certain range of values.
While so far we have created symbolic variables and assigned expressions
to symbolic variables, it is also possible to create symbolic functions. Symbolic
functions look and act just like a function you might see in your mathematics
course.
>> f(x) = 4*x^(4)-2*x+3
f(x) = 4*x^4 - 2*x + 3
15.2 Limits
Matlab provides the function limit to calculate the limits of functions. In its basic
form limit takes three arguments: (1) the function, (2) the symbolic variable that
you would like to look at a limiting value of, and (3) the value of the limiting value,
which may include inf for ∞. Using standard notation, limit(f,x,inf) would
correspond to:
lim f (x)
x→∞
desired problem. This will be followed by actual Matlab input and output used. I
will start off by clearing variables and creating a symbolic variable x.
lim x
x→−4
>> syms x
>> assume(x,'real')
>> l1 = limit(x,x,4)
l1 = 4
lim x 2
x→2
>> l2 = limit(x^2,x,2)
l2 = 4
x2 + x + 2
lim
x→1 x +1
>> l3 = limit((x^2+x+2)/(x+1),x,1)
l3 = 2
p
lim x
x→2
>> l4 = limit(sqrt(x),x,2)
l4 = 2^(1/2)
>> l5 = limit(x*cos(x),x,pi)
l5 = -pi
e 2x − 1
lim
x→0 x
>> l6 = limit((exp(2*x)-1)/x,x,0)
l6 = 2
ln x
lim
x→∞ x
>> l7 = limit( log(x)/x,x,inf)
l7 = 0
0.8
0.6
0.4
0.2
-0.2
-0.4
-0.6
-0.8
-1
-1 -0.5 0 0.5 1
df f (x + ∆x) − f (x)
= f 0 (x) = lim
dx ∆x→0 ∆x
Or equivalently we will use the following notation where ∆x = h:
df f (x + h) − f (x)
= f 0 (x) = lim
dx h→0 h
Using Matlab, we can calculate the differential of a function using this def-
inition and limit. Here I will start by clearing variables since we will need an
additional symbolic variable h. We will consider cases for which I can find the
differential in the front cover of my Calculus textbook.
d x
e = ex
dx
516 Chapter 15 An Introduction to Symbolic Calculations
>> syms x h
>> d1 = limit((exp(x+h)-exp(x))/h,h,0)
d1 = exp(x)
d x
b = b x ln (b)
dx
>> syms b
>> d2 = limit( (b^(x+h)-b^(x))/h,h,0)
d2 = b^x*log(b)
15.3 Derivatives
While we have just shown that we can use limit to evaluate derivatives, this
is more easily accomplished using the function diff. In its basic form, diff
takes two input arguments: (1) the function and (2) the (symbolic) variable to
differentiate with respect to. We can also pass an optional third parameter, a scalar
to indicate the order of differentiation. Let’s demonstrate using a few examples. I
will again begin by clearing all variables.
d x
e = ex
dx
>> syms x
>> d1 = diff(exp(x),x)
d1 = exp(x)
d x
b = b x ln (b)
dx
>> syms b
>> d2 = diff(b^x,x)
d2 = b^x*log(b)
15.4 Indefinite and Definite Integrals 517
Nice! Know that Matlab also works very nicely with symbolic functions, which
eliminates the need for subs.
>> f(x) = exp(x);
>> f(2)
ans = exp(2)
>> Df = diff(f,x)
Df(x) = exp(x)
>> Df(2)
ans = exp(2)
where note that x has already been defined as a symbolic variable. Instead of just
assigning exp(x) to a symbolic variable f as we did at the start of this chapter,
now we have a symbolic function with an explicit input of x.
1
Z
xd x = x 2 + c
2
518 Chapter 15 An Introduction to Symbolic Calculations
>> i1 = int(x,x)
i1 = x^2/2
Of course too we could compute the definite integral by taking the difference in
the antiderivative evaluated at the two limits.
>> i2 = int(x,x)
i2 = x^2/2
Cool! What if you knew the lower limit was 0, but wanted to consider various
values of the upper limit?
>> syms b
>> i4(b) = int(x,x,0,b)
i4(b) = b^2/2
>> i4(2)
ans = 2
Building off of this, what if you wished to solve for the value of b such that
Z b
xd x = 11
0
For that, we can readily use Matlab’s symbolic solve function. The function
solve requires just two inputs: (1) the equation of interest, and (2) the symbolic
variable you wish to solve for. Before we see this in action, I would like to remind
you that in Matlab = corresponds to a variable assignment, while == corresponds
to equivalence.
>> bsol = solve(i4 == 11,b)
bsol =
15.5 More fun with solve 519
22^(1/2)
-22^(1/2)
Excellent! Notice that with solve, I do not need to set-up the function so that it is
equal to 0 as we did with fzero or fsolve. Now what if we were told that b was
greater than 0? Well, we can use assume to help.
>> assume(b>0)
>> bsol = solve(i4 == 11,b)
bsol = 22^(1/2)
Know that I like the notation assume(b>0) as it can be used in general. However,
Matlab also has a keyword for this case: assume(b, ’positive’).
ax 2 + bx + c = 0
p
−b ± b 2 − 4ac
x=
2a
>> clear variables
>> syms a b c x
>> f = a*x^2 + b*x + c == 0
f = a*x^2 + b*x + c == 0
>> qf = solve(f,x)
qf =
-(b + (b^2 - 4*a*c)^(1/2))/(2*a)
-(b - (b^2 - 4*a*c)^(1/2))/(2*a)
Here we were substituting multiple variables at once, so we list them all out as a
vector. Nice! We could have instead used a symbolic function. Try this:
>> qf(a,b,c) = solve(f,x)
qf(a, b, c) =
-(b + (b^2 - 4*a*c)^(1/2))/(2*a)
-(b - (b^2 - 4*a*c)^(1/2))/(2*a)
Fantastic!
So far we have made solve look like an all start. It is important to show some
limitations. Consider the following equation from Exam 2:
We were asked to solve for the first five positive roots. To limit our range of search,
we could plot and see over what range the first five roots occur over. We could
then use assume to set the range of x values. Here I will just arbitarily set the
range, and then could always update the upper limit (that is, the upper assume)
to change the range. I will start by clearing my variables in case I had made any
assumptions previously.
>> syms x
>> assume(x>0)
>> assume(x<20)
>> eq = cos(x)*cosh(x) == 1;
>> sol = solve(eq,x)
Warning: Unable to solve symbolically. Returning a numeric solution using
vpasolve.
> In solve (line 304)
sol = 0
So there you have it. No analytic solution exists, so it is solving numerically. And
when it solves numerically, just like before we get just one root at a time. To get a
different value, my search range needs to change. For example
>> assume(x>1)
>> sol = solve(eq,x)
Warning: Unable to solve symbolically. Returning a numeric solution using
vpasolve.
> In solve (line 304)
sol = 20.420352245626061090936411189313
15.6 Factor my polynomial please 521
x 2 − 2x − 8 = (x + 2) (x − 4)
>> f = x^2-2*x-8;
>> fs = factor(f,x)
fs = [ x + 2, x - 4]
The last “trick” I will mention is that Matlab can intelligently simplify an
expression of interest. Please have a look at the Matlab documentation for
simplify. It includes some interesting examples, including the ability to work
with units. Yes, units! Here are a couple of examples to give you an idea:
>> m = (x^2 + 5*x + 6)/(x + 2);
>> ms = simplify(m)
ms = x + 3
and rref previously. To see this in practice, let’s revisit our first example from
Chapter 8:
y = −x − 3
x 2 + y 2 = 17
>> syms x y
>> eq1 = y == -x-3;
>> eq2 = x^2 + y^2 == 17;
>> [solx,soly] = solve([eq1,eq2],[x,y])
solx =
1
-4
soly =
-4
1
And just like that we get both solutions in a single call, no initial guess necessary.
Also, notice that we did not even need to re-arrange our equations into the form
of an error function. Nice!
Again, so far we have looked at rather “simple” problems using our symbolic
solver. Try to solve the following system of equations which you encountered on
Exam 2:
¡ ¢ ¡ ¢
sin x + y − cos y = 0.17
¡ ¢
cos x − y + sin (x) = 1.8
df
=af
dt
No problem, the ODE is separable and I can readily separate and integrate. Be-
yond the separable case, if you are lucky, you can recast the ODE into its general
form, and then look-up the general solution. Fortunately for us, MATLAB can
symbolically solve ODEs too!
To demonstrate, I will resolve some of our previous examples we solved nu-
mericall. Let’s consider first the example just mentioned. I’ll start by clearing
variables and then finding the general solution:
>> clear variables
>> syms f(t) a
>> ode = diff(f,t) == a*f
ode(t) = diff(f(t), t) == a*f(t)
15.8 Initial Value Ordinary Differential Equations 523
First, note that with the syms command I have f(t). This creates the symbolic
function f, and additionally the symbolic variabl t since since f is a function of t.
Here I also have a, which is an arbitary parameter. The differential equation is
solved with the function dsolve.
If a was known, you could readily using subs to provide its value. To solve
for the constant of integration, here C3, you could use solve followed by subs.
Interestingly, for our first order ODE we need just know the value of our function
at some t , not necessarily t = 0. But what if for example we were told:
f (t = 0) = 100
>> syms C3
>> Ci = solve(fsol(0) == 100, C3)
Ci = 100
Notice that I needed to first needed to indicate that the constant of integration,
C3, was symbolic. We could just as well do all of this with a single dsolve call:
>> clear variables
>> syms f(t) a
>> ode = diff(f,t) == a*f
ode(t) = diff(f(t), t) == a*f(t)
df
((t ) = a f (t ) [1 + sin (ωt )]
dt
Where a = 0.01, ω = 2π/365, and f (t = 0) = 2
>> clear variables
>> syms f(t)
>> a = 0.01;
>> omega = 2*pi/365;
>> ode = diff(f,t) == a*f*(1+sin(omega*t))
524 Chapter 15 An Introduction to Symbolic Calculations
>> ic = f(0) == 2;
>> sol(t) = dsolve(ode,ic)
sol(t) = 2*exp(t/100 - (73*cos((2*pi*t)/365))/(40*pi))*exp(73/(40*pi))
Incredible! When we solved numerically, we found that at 365 days we had 76.9530
rats. Let’s see if we get the same value.
>> sol365 = sol(365)
sol365 = 2*exp(73/(40*pi))*exp(73/20 - 73/(40*pi))
Amazing.
We can also use dsolve to solve higher-order ODEs. Remember when we
solved higher-order ODEs numerically, we first thad to re-write the equation as a
series of first order ODEs. That is not necessary here. Let’s consider our free fall
example.
d 2z
(t ) = z 00 (t ) = −g
d x2
where z (t = 0) = z 0 and z 0 (t = 0) = v 0 .
>> clear variables
>> syms z(t) g z0 v0
>> ode = diff(z,t,2) == -g
ode(t) = diff(z(t), t, t) == -g
Fantastic! Note that for the second initial condition, z 0 (t = 0) = v 0 , we first needed
to create a symbolic function for the differential of z with respect to t , so then we
could evaluate it at t = 0.
If you also wanted velocity, we could get it two ways. First, we could just
differentiate our solution for z (t ).
>> vsol = diff(zsol,t)
vsol(t) = v0 - g*t
z 0 (t ) = v (t ) v 0 (t ) = −g
15.9 Boundary Value Problems 525
where z (t = 0) = z 0 and v (t = 0) = v 0 .
>> clear variables
>> syms z(t) v(t) z0 v0 g
>> ode1 = diff(z,t) == v
ode1(t) = diff(z(t), t) == v(t)
A perfect match!
Here we will see how we can readily solve boundary value problems symbolic,
exactly as we solved initial value ODEs. For our diffusion example:
>> clear variables
>> syms cA(x) l cA0 cAl
>> ode = diff(cA,x,2) == 0;
>> b0 = cA(0) == cA0;
>> bl = cA(l) == cAl;
>> solC(x) = dsolve(ode,[b0,bl])
solC(x) = cA0 - (x*(cA0 - cAl))/l
This is the analytic solution we would have obtained if we have solved by hand.
We can solve harder cases too. Let’s again consider the case of the diffusion
of dilute A through a thin film of B , but now let’s assume that A undergoes a
homogeneous (bulk volumetric) reaction, where we will assume the reaction is
first order in A, r A = −kC A . This leads to the following at steady state:
d 2C A k
− ◦ CA = 0
d x2 D AB
C A (x = 0) = C A,0
C A (x = l ) = C A,l
where D ◦AB is the diffusion coefficient of dilute A in B .
>> clear variables
>> syms cA(x) l cA0 cAl k DAB
>> ode = diff(cA,x,2) - (k/DAB)*cA == 0;
>> b0 = cA(0) == cA0;
>> bl = cA(l) == cAl;
>> solC(x) = dsolve(ode,[b0,bl])
This is great and correct, but it would really be nice if we could simplify this
expression at all. If we apply simplify here, the solution does not change. The
issue is we have terms of (DAB*k)ˆ (1/2). Remember that the square root of a
negative number is imaginary. To make sure we are keeping it real, we can use
assume where we know that both quantities are positive.
>> assume(k>0)
>> assume(DAB>0)
>> solC(x) = simplify(solC)
solC(x) = -(cA0*exp((2*k^(1/2)*l)/DAB^(1/2)) - cAl*exp((k^(1/2)*l)/DAB^(1/2)
cAl*exp((k^(1/2)*(l + 2*x))/DAB^(1/2)))/(exp((k^(1/2)*x)/DAB^(1/2)) - exp((k^
15.9 Boundary Value Problems 527
That’s a big improvement! To shorten it up, let’s introduce α = k/D ◦AB . Note to get
it to work here, I need to use α = k 1/2 /D 1/2
AB
.
>> solC = subs(solC, k^(1/2)/DAB^(1/2), alpha)
solC(x) = -(cA0*exp(2*alpha*l) - cAl*exp(alpha*l) - cA0*exp(2*alpha*x) +
cAl*exp(alpha*(l + 2*x)))/(exp(alpha*x) - exp(alpha*(2*l + x)))
When solving by hand I could further simplify the equation and take advantage
of the relation that
1¡
sinh (z) = e z − e −z
¢
2
But try as I might with Matlab, I could not get it to simplify further. I was finally
able to get it by telling Matlab that x and l are both greater than 0, and also using
an additional agrument with simplify to increase the number of simplification
steps used.
>> assume(l>0)
>> assume(x>0)
>> solC(x) = simplify(solC,'Steps',100)
solC(x) = (cAl*sinh(alpha*x) + cA0*sinh(alpha*l - alpha*x))/sinh(alpha*l)
Beautiful!
Appendix A
while loops
for i=n0:n
statement
end
We enter the loop with i=n0, perform the statement, and then check to see if
i==n. If yes, we are done, exit the loop. If no, increment i by 1 and repeat. If
we wish to increment by a value other than 1, we can using the extended colon
operator:
for i=n0:dn:n
statement
end
where dn corresponds to the desired increment value. We have also seen that
we can exit a loop before the specified number of iterations using the break
statement. In Section 5.12 we were introduced to the continue statement. When
the continue statement is encountered, we jump immediately to the next loop
iteration.
529
530 Chapter A while loops
with the for loop. Once you have mastered the for, there in principle is no need
for the while.
Choosing one over the other is a matter of programming style. When I was an
undergraduate student my research advisor told me never to use for loops, that
while loops were much better. As a graduate student, the philosophy of my group
was the opposite, for loops were preferred over while loops. So in addition to
your own preferred programming style, knowing both is good because a future
collaborator may have a different style than you.
After all that talk, let’s look at the structure of a while loop and a few examples
we have already encountered.
The basic structure of a while loop is:
This is best seen with an example. Let’s start by writing a function with a for loop
to sum the elements of a vector.
As compared to using a for loop, here we update our own index variable each
iteration. Notice that my conditional statement is i < length(X). You would
think it should be i <= length(X). But that would result in an error. MATLAB
will only check the conditional statement at the start of a new iteration, before we
update our index variable.
How my undergraduate advisor used to use while loops which did provide
some additional flexibility was to use a “flag” variable. Before the loop, the flag
variable is initialized with some value, say zero. Then when you want to stop/exit
the loop, you just change the value of flag to a different value. This gives flexibil- t Remember, the while state-
ity when there are multiple different reasons to exit a loop without needing to ment executes while the
conditional statement is
remember the break command. Here is how we could use a flag variable in our
true. You could therefore
summation example: just assign flag a value
of one initially, flag = 1.
Listing A.3 sum_flag.m Since to MATLAB a value
of 1 means true, your con-
1 function res = sum_flag(X) ditional statement could
2 c=0; be just flag. Then when
3 i=0; you want to exit, assign to
4 flag = 0; flag a value of zero, false.
5 while flag == 0 To me, explicitly having a
6 i=i+1; logical statement of flag
== 1, while using more
7 c=c+X(i);
characters, makes my code
8 if i == length(X) easier to understand. And
9 flag = 1; if it is easier to understand,
10 end I am less likely to make a
11 end mistake.
12 res = c;
13 end
>> Y = 1:6;
>> sum_for(Y)
ans = 21
>> sum_while(Y)
ans = 21
>> sum_flag(Y)
ans = 21
All three loops and methods are identical. I encourage you to try them all and
decide which you prefer. Develop your own style.
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp
spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp spsp