Scientific Computing Using Python - PHYS:4905

Lecture #03 - Prof. Kaaret

Programming: scripts, branching, looping

Scripts/Programs

The textbook does loops before scripts, but as soon as you do more than one line of Python, it is a lot easier to use a script rather than the command line.

Before we start with the editor, check what directory you are in by typing pwd on the command line.  pwd stands for print working directory.  You may want to move to a directory where you will keep all of your Python stuff.  If so, use the command cd for change directory.  You can also use the mkdir command if you want to make a new directory.

The left pane in Spyder is usually set up as the editor pane.  If you open a new file, Spyder will automatically insert the template that we edited in lecture 1.  You might want to add a line to the template import numpy in your favored style.

Now enter a line

print ("Hello world!")

then save the file with the file name 'test1.py'.  While you are saving, check that the directory matches the directory that you found with pwd on the command line.

Go back to the command line and type

run test1

Your script or program should print out

Hello World!

Congratulations, you're now a programmer!

The editor in Spyder has some useful Python-specific features.  Go to the editor and type

= 7*8

and then move off the line.  A red circle with a white x in it should appear.  Move your cursor over the circle and a popup window with a message that you have a syntax error should appear.  The editor is giving you advance warning that Python will complain about your code. Now hit the green triangle above the editor pane.  This is another way to run the program.  You should get a message in the console pane that it is running your program followed by a message that you have a syntax error and tells you which line has the error.  Sometimes an error on a preceding line can cause Python to find a syntax error.  So, if Python tells you that you have an error on a line of your code and you can't figure out what it is, look at the preceding line.

Now remove the line with the syntax error and write a new line

x = np.arange(-3, 15, 2)

Then run the script.  You have created an array x with a bunch of values.  If you want to inspect those values, you can go to Spyder's upper right pane and click on the 'Variable explorer' tab.  If you double click on the Value box, you'll be rewarded with a pop-up window where you can inspect and modify the array contents.


Good Programming Practice

Comments - A good programmer includes lots of comments in her code.  Comments are useful to other people trying to read your code and they are also useful when you try to read your own code a few months or more after you have written it.  It is good practice to write the comments before you write the code.  That way you'll have to lay out your thinking before you start coding.

Python has two ways to write comments.  Python will consider any text following a # to be a comment. You can include comments on the same line as code

g = 9.8 # [m/s^2]  gravitational acceleration at the Earth's surface

or have whole lines that are comments.

# gravitational acceleration at the Earth's surface
g = 9.8 # [m/s^2]

When doing computations with physical quantities, it is good to always specify the units of any numbers with a comment.

The second way to write a comment is to use """.  All of the lines until the next """ will be considered a comment.

""""
This is a program to calculate the trajectory of a object in the Earth's gravitational field. The program takes as input the initial velocity of the object specified as a speed and angle. It calculates the distance that the object will travel before returning to Earth.
Units are in SI.
"""

Small pieces - Your program is more likely to work correctly if you break it up into small pieces.  Test each new piece of your code to make sure that it does what you want.  Input test case parameters for which you know the answer or can at least recognize grossly incorrect answers.  Inspect variables at each step using print statements or the Variable explorer. 

Comment your variable definitions - People sometimes comment the steps of their code, but forgot to provide comments about the inputs to the procedures or about how variables are used.  The textbook suggests using long, descriptive variable names.  This is useful if you have large blocks of code, but I try to break my code into small pieces and then provide a comment where each variable is defined.  The choice is up to you

initial_speed = 12.0 # [m/s]

or

v0 = 12.0 # [m/s] initial speed

The disadvantage of using short variable names is that you may occasionally re-use the same name, which will mess things up.  This happens most often with loop or index variables like i and j.

Comment other people's code - If you find yourself using other people's code, the first thing to do is to go through it and add comments if it is not well commented.  This will help you understand the code now, when you are trying to use it, and in the future, if you need to use it again.  You will do this in the homework assignment for this lecture.

Conditional execution

The if statement lets you run different code depending on the values of variables in your program. Let's write a short program to play a simplified version of the dice game Craps.  You roll two dice.  If the sum of the two dice is 7 or 11 then you will win.  If it is 2, 3, or 12 then you lose.  For any other value, you roll again.  Copy the following into you editor window, save as craps.py, and run it.

# play simplified Craps
# this code is not properly commented
d1 = np.random.randint(1,7)
d2 = np.random.randint(1,7)
s = d1+d2
if ((s == 7) or (s == 11)) :
  print('Roll is '+str(d1)+' and '+str(d2)+'.  You win!')
elif ((s == 2) or (s == 3) or (s == 12)) :
  print('Roll is '+str(d1)+' and '+str(d2)+'.  You lose.')
else :
  print('Roll is '+str(d1)+' and '+str(d2)+'.  Roll again.')


Let's look at the program.  Lines 1 and 2 are comments.  Line 2 shows that a lazy individual wrote the code and you should take it upon yourself to correct for his laziness. 

Lines 3 and 4 generate random integers between 1 and 6.  These are the rolls of the two individual die.  Note that the range is from 1 to 6.  The randint function follows same Python convention that we saw for slices where the range includes the first number, but ends at one less than the last number.  The randint function is part of the random sampling submodule within numpy.  Numpy has so many functions that they need to be split up. 

Line 5 calculates the sum of the two random numbers generates on lines 3 and 4.  This is the 'roll'.

Line 6 checks if you have won by rolling a 7 or 11.  Note the colon at the end of the if statement.  A colon is required for all Python statements that control program execution.

If line 6 shows that you won, then line 7 shares that information with you.  How does Python know which statements to execute if the condition is true?  Python relies completely on indentation.  The first line after the if statement must be indented by at least one space.  That line and any other lines with the same indentation will be executed if the condition in the if statement is true.  The Python standard is to indent by four spaces.  I prefer to use two spaces because I find it more readable for very nested code

Lines 8 and 9 check if you have lost by rolling a 2, 3, or 12 and then inform you if so.  The word elif is a contraction of else if.  Line 8 is executed only if the condition on line 6 was false.  Note that the indentation of elif must match that of the corresponding if statement.

If you haven't won or lost, then lines 10 and 11 tell you to roll again.  Line 11 is executed only if the conditions on all of the preceding if and elif statements were false.    Note that the indentation of else must match that of the corresponding if statement.

If the 'then' part of an if statement is very short, you can put it on the same line as the if statement.  This also works for elif and else statements.

if ((s == 7) or (s == 11)) : print('Roll is '+str(d1)+' and '+str(d2)+'.  You win!')


Testing the equality of floats - Sometime you will run into issues when you are trying to check if two floating point numbers are equal. Try

(0.1+0.1+0.1) == 0.3

You get false, while you expect that you should get true.  This is because Python uses binary to represent floating point numbers and the binary representation of 0.1 is an infinitely repeating string of bits (binary digits).  It is better to use inequalities when comparing floats.  If you really need to check for equality, do it to some level of accuracy, e.g.

ftol = 1E-6 # tolerance when comparing two floats for equality
abs((0.1+0.1+0.1) - 0.3) < ftol

It is better to specify the tolerance as a fraction of the value,

ftol = 1E-6 # tolerance when comparing two floats for equality
s, v = 0
.1+0.1+0.1, 0.3
a
bs(s-v) < v*ftol


Loops

For loops - Go to your editor window and copy in the following code.

# find squares from 1 to 9 and their sum
s = 0
for x in np.arange(1, 10) :
  print(' The square of ', x, ' is ', x**2)
  s = s + x**2
print("We're done, s = "+str(s))

Now save and run the program.  The for statement makes a loop, note the colon at the end.  The variable x is assigned, in turn, to each value produced by the arange(10) function call, which are the integers from 1 to 9.  The statements in the body of the loop are executed for each new value of x.

To determine which statements are in the body of the loop and should be executed multiple times, Python relies on indentation, just like for if statements.  The first line after the for statement must be indented by at least one space.  All of the following lines in the body of the loop must have the same indentation.  The first line with the same indentation as the for statement marks the end of the loop body.

If the body of a loop is very short, you can put it on the same line as the for statement.

for x in np.arange(1, 10) : s = s + x**2


While loops -
Sometimes you need to repeat lines of code, but with variables that don't increment in a simple fashion.  For that you use a while loop.  Let's write a bit of code to do a random walk, starting at a displacement of 0 and ending when the magnitude of the displacement reaches 5.  The following code should do the job

# do a random walk with steps of -1, 0, 1 up to a specified maximum displacement
maxd = 5 # maximum displacement

n = 0 # n is the number of steps taken, start at 0

d = 0 # d is the current displacement, also start at 0

while abs(d) < maxd : # loop while the magnitude of the displacement is less than maxd

  d = d + np.random.randint(-1,2) # add a random step (-1, 0, or +1) to displacement

  n = n + 1 # incremement the step counter

  print('At step '+str(n)+' position is '+str(d))

print('It took '+str(n)+' steps to reach a displacement of '+str(d))

Now let's try a much larger maximum displacement, set maxd = 1000 and run the code again.  The code takes a while to run, so let's get bored and stop it.  To stop the code move into the console window and type Ctrl-C.  You should get a long list of messages that end with 'KeyboardInterrupt'.
Most of the time in the loop is taken by the print statement.  Let's "comment out" the print statement by putting a # in front of it.  The # can go either just before print or at the start of the line.  I prefer to place the # just before print so that the indentation stays the same.  Now run the code.  Without the print, it executes quickly.


Breaking out of loops - You can exit a loop, either for or while, when a special condition is met by using a break statement.  For example, if you don't want to let the random walk above run too long, you could add a line after the where you increment the step counter
  if (n > 10000) : break
This will exit the while loop if you reach 10,000 steps.

User input

The input function enables a program to ask for input from the user.  Note that Python 2 and 3 differ in their input functions.  If you are using Python 2, make sure that your programs include the line
input = raw_input
to make input work in the Python 3 manner. 

The input function prints out the text in the argument and returns the text typed in by the user as a string.  You then convert the string to the form that you need using a function like int or float.
Modify the random walk program by commenting out the line where a value for maxd is assigned and replacing it with 

maxd = int(input("Enter maximum displacement "))

Now when you  run the program, it will ask you for the maximum displacement.


Array operations

When using Python and NumPy in particular, it is best to avoid using for loops whenever possible.  Your code will likely run faster if you can avoid using a loop.  NumPy allows operations on whole arrays.  The loop that we wrote at the beginning of the class could be re-written as

s = np.sum(np.arange(1, 10)**2)

This is shorter to write and runs much faster.

Array operations require the arrays to have the same shape.  If you do an operation with a float and an array, the float is 'broadcast' to have the same shape as the array meaning an array is constructed with the same shape and each value set to the float.  In this context, floats or integers are sometimes called 'scalars'.

Most array operation operate separately on each element of the array.  For example,

a = np.array([1.0, 2.0, 3.0])
b = np.array([3.0, 2.0, 0.5])
a * b

produces an array with the same number of elements with a and b with each element equal to the product of the two corresponding elements in a and  b.

NumPy also includes functions for standard vector and matrix math.  The dot product of two vectors is

np.dot(a, b)

The product of a matrix times a vector is

v = np.array(np.diag([1,2,3]))
np.dot(v,a)


Assignment

The remainder of today's class will be devoted to working through chapter 3 of the textbook where all of these topics are covered.  As you read the chapter,  enter the commands in the purple highlights into Spyder and see what you get.  When you are done with the chapter, do the third assignment.

HW #3 is due at the beginning of the next class.  HW #3 contains the first real programming tasks.  If you haven't programmed before, it may be difficult.  If you find that you can't do the assignment, please tell me and we'll figure out how to get you help with programming.