In Python we often need to repeat the same calculation multiple times. A few variable values may change, but the same general flow of computing is performed each time. Rather than copying the code multiple times in a script, we can use a for loop to contain the code that is repeated.

The Python documentation has a guide on for Statements.

Syntax

For loops are created in Python using this syntax:

for <iterator> in <sequence>:
    code to execute multiple times ...

After the for keyword is the name of a variable that changes value on each pass of the loop. It is assigned to each value in the sequence on the right side of in, one at a time, in order. As with if statements, the code inside a for loop is marked by indentation rather than an end keyword, and the colon : at the end of the for line is required. For example, this for loop prints the powers of two up to 1024:

for p in range(11):
    x = 2**p
    print(x)

1
2
4
8
16
32
64
128
256
512
1024

range(11) produces the sequence of whole numbers from 0 up to (but not including) 11, so the loop above runs with p equal to each value from 0 through 10.

Looping Over a List

One of the most common uses of for loops in Python is to perform a series of calculations for each element in a list. Unlike MATLAB, plain Python does not have built-in vectorized math, so looping over a list is the normal way to process it. (Once you’ve learned about NumPy arrays, later in the course, some of these calculations can be vectorized similarly to MATLAB. See NumPy Arrays.)

import math

measurements = [1.0, 4.0, 9.0, 16.0, 25.0]

for value in measurements:
    print(math.sqrt(value))

1.0
2.0
3.0
4.0
5.0

Here, value takes on each number in measurements in turn, and math.sqrt(value) is computed and printed on every pass. Lists are covered in more detail on the Lists & Tuples page. For now, a list is just a sequence of values written between square brackets and separated by commas.

Example: Rocket Engine with Inexact Performance

Question

Often in the design phase of engineering, the exact values we need for an analysis are not immediately available. My work depends on another person’s results, their work depends on a third person’s results, and that third person’s work depends on my results. To break the cycle, we need to be able to handle estimated ranges for values rather than the exact value.

Consider, for example, the design of an interplanetary rocket that will send a satellite from Earth to Mars. We need to determine the fraction of the rocket’s mass that will be fuel for the burn - its propellant mass fraction $\zeta$ - using the rocket equation:

\[\Delta v = I_{sp} g_0 \log\left(\frac{m_0}{m_f}\right)\]

The propellant mass fraction is defined as $\zeta = 1 - m_f/m_0$. We are given that the $\Delta v$ for the burn is 12 km/s and Earth’s surface gravity is $g_0$=9.81 m/s2. The specific impulse, $I_{sp}$, is somewhere between 380 and 450 s. The design of the rocket is not mature enough for a precise value of $I_{sp}$, but we still need to produce values. Determine the minimum and maximum propellant mass fraction for the rocket.

Solution

To solve this problem, we isolate the mass fraction term in the rocket equation then calculate the propellant mass fraction. We do this for the lower and upper bounds on the specific impulse, keeping a running minimum and maximum as the loop goes. Rearranging the rocket equation:

\[\frac{m_0}{m_f} = e^\frac{\Delta v}{I_{sp} g_0}\]

We then plug that fraction into the propellant mass fraction formula, and repeat the process for the lower and upper bounds on specific impulse.

import math

# Givens
delta_v = 12000  # m/s
g0 = 9.81  # m/s^2

Isp_values = [380, 450]

zeta_min = None
zeta_max = None
for Isp in Isp_values:
    m0_over_mf = math.exp(delta_v / (Isp * g0))
    zeta = 1 - 1 / m0_over_mf
    if zeta_min is None or zeta < zeta_min:
        zeta_min = zeta
    if zeta_max is None or zeta > zeta_max:
        zeta_max = zeta

print("Propellant mass fraction range:")
print(f"{zeta_min:.4f} to {zeta_max:.4f}")

Propellant mass fraction range:
0.9340 to 0.9600

Nested For Loops

A nested for loop is when a for loop is contained within another for loop. This structure is useful when iterating over multiple dimensions of data, or performing operations with multiple levels of repetition. For example, you could use a for loop to propagate the trajectory of an airplane, then nest that within another for loop where you vary the wind speed, the number of passengers, the air temperature, etc.

Python can nest for loops to any depth, however the compute time will grow rapidly as more loops are added. In the example above, if I have 1,000 timesteps and 30 wind speeds, then Python will perform the inner loop calculations 30,000 times. If there are 15 different cases of passenger count, I could loop over that as well, which would bring the number of inner loop calculations to 450,000. This increase in the number of calculations is the curse of dimensionality and drives the need to make calculations as fast as possible. Each nested for loop needs its own consistent indentation, one level deeper than the loop it’s nested inside.

Reading Questions

  1. How does Python determine how many times to execute the code in a for loop?
  2. What are the components of the syntax of a for loop?
  3. What values does range(5) produce, and how many times would a for loop over it execute?
  4. How does Python know where the body of a for loop ends, since it doesn’t use an end keyword?
  5. Can a for loop be executed within another for loop?

Practice Problem: Cumulative Fuel Burn

Your Task

Write a script named fuel_burn_loop.py that starts from the given values:

fuel_start_lb = 3000
hourly_burn_lb = [450, 430, 410, 395]

Use a for loop over hourly_burn_lb to compute:

  1. fuel_remaining - a list containing the fuel remaining after each hour, in order (start from fuel_start_lb and subtract each hour’s burn as you go)
  2. total_burned_lb - the total fuel burned over all hours

Your variable names for the two answers above must match exactly (fuel_remaining, total_burned_lb) so that the checker below can find them.

Checking Your Work

Download check_fuel_burn_loop.py and save it in the same folder as your fuel_burn_loop.py script. Open a terminal in that folder and run:

python check_fuel_burn_loop.py

The checker runs your script and reports whether each of the two values is correct. This is practice, not a graded assignment. If something doesn’t pass, use the feedback to find and fix the issue, then run the checker again.