Worked Examples: Advanced Loop Control#

These worked solutions correspond to the exercises on the Advanced Loop Control page.

How to use this notebook:

  • Try each exercise yourself first before looking at the solution

  • The code cells show both the code and its output

  • Download this notebook if you want to run and experiment with the code yourself

  • Your solution might look different - that’s fine as long as it gives the correct answer!

Exercise 1: Temperature-Dependent Reaction Rate Study#

Problem: You are investigating how reaction rate changes with temperature. Your automated system heats a reaction vessel from 20°C to 100°C in 8°C increments, taking a measurement at each temperature.

Write a while loop that:

  • Starts with temperature = 20 (°C)

  • Takes a measurement (just print the temperature for now)

  • Increases temperature by 8°C

  • Continues until temperature reaches or exceeds 100°C

Print each measurement temperature and count the total number of measurements taken.

Solution:

temperature = 20  # Starting temperature in °C
increment = 8  # Temperature increase per step
measurement_count = 0  # Counter for measurements

while temperature <= 100:
    measurement_count += 1
    print(f"Measurement {measurement_count}: {temperature}°C")
    temperature += increment

print(f"\nCompleted {measurement_count} measurements from 20°C to {temperature - increment}°C")
Measurement 1: 20°C
Measurement 2: 28°C
Measurement 3: 36°C
Measurement 4: 44°C
Measurement 5: 52°C
Measurement 6: 60°C
Measurement 7: 68°C
Measurement 8: 76°C
Measurement 9: 84°C
Measurement 10: 92°C
Measurement 11: 100°C

Completed 11 measurements from 20°C to 100°C

How this works:

  • The while loop continues as long as temperature <= 100

  • Each iteration:

    • We increment the measurement counter

    • Print the current temperature

    • Increase the temperature by 8°C

Note: The final print statement shows the last measurement temperature (temperature - increment) rather than the current value of temperature, which is increased to 108°C in the final loop iteration.

print(temperature)
108

Exercise 2: Convergence with Iteration Limit#

Problem: The cooling simulation from the example converges relatively quickly because it loses 10% of the temperature difference each step. However, if the cooling rate is slower, convergence can take many iterations.

Modify the simulation to use a slower cooling rate (only 2% per step instead of 10%). This is more realistic for a well-insulated vessel. However, since convergence is now much slower, add an iteration limit of 100 steps. If equilibrium isn’t reached within 100 steps, stop and report that more time is needed.

Solution#

temperature = 100.0  # Starting temperature in °C
room_temperature = 20.0  # Ambient temperature in °C
threshold = 0.5  # Stop when within 0.5°C of room temp
cooling_rate = 0.02  # Cool by 2% per step
iteration = 0
max_iterations = 100

# Check if we're already at equilibrium before starting the loop
converged = temperature - room_temperature <= threshold

while not converged and iteration < max_iterations:
    # Cool by 2% of the temperature difference each step
    temperature_difference = temperature - room_temperature
    temperature = temperature - cooling_rate * temperature_difference
    iteration += 1
    print(f"Step {iteration}: Temperature = {temperature:.2f}°C")
    
    # Check if we've reached equilibrium
    if temperature - room_temperature <= threshold:
        converged = True

# Report results based on whether convergence was achieved
if converged:
    print(f"\nThermal equilibrium reached after {iteration} steps")
    print(f"Final temperature: {temperature:.2f}°C")
else:
    print(f"\nIteration limit reached after {iteration} steps")
    print(f"Current temperature: {temperature:.2f}°C (still {temperature - room_temperature:.2f}°C above room temperature)")
Step 1: Temperature = 98.40°C
Step 2: Temperature = 96.83°C
Step 3: Temperature = 95.30°C
Step 4: Temperature = 93.79°C
Step 5: Temperature = 92.31°C
Step 6: Temperature = 90.87°C
Step 7: Temperature = 89.45°C
Step 8: Temperature = 88.06°C
Step 9: Temperature = 86.70°C
Step 10: Temperature = 85.37°C
Step 11: Temperature = 84.06°C
Step 12: Temperature = 82.78°C
Step 13: Temperature = 81.52°C
Step 14: Temperature = 80.29°C
Step 15: Temperature = 79.09°C
Step 16: Temperature = 77.90°C
Step 17: Temperature = 76.75°C
Step 18: Temperature = 75.61°C
Step 19: Temperature = 74.50°C
Step 20: Temperature = 73.41°C
Step 21: Temperature = 72.34°C
Step 22: Temperature = 71.29°C
Step 23: Temperature = 70.27°C
Step 24: Temperature = 69.26°C
Step 25: Temperature = 68.28°C
Step 26: Temperature = 67.31°C
Step 27: Temperature = 66.37°C
Step 28: Temperature = 65.44°C
Step 29: Temperature = 64.53°C
Step 30: Temperature = 63.64°C
Step 31: Temperature = 62.77°C
Step 32: Temperature = 61.91°C
Step 33: Temperature = 61.07°C
Step 34: Temperature = 60.25°C
Step 35: Temperature = 59.45°C
Step 36: Temperature = 58.66°C
Step 37: Temperature = 57.88°C
Step 38: Temperature = 57.13°C
Step 39: Temperature = 56.38°C
Step 40: Temperature = 55.66°C
Step 41: Temperature = 54.94°C
Step 42: Temperature = 54.24°C
Step 43: Temperature = 53.56°C
Step 44: Temperature = 52.89°C
Step 45: Temperature = 52.23°C
Step 46: Temperature = 51.59°C
Step 47: Temperature = 50.95°C
Step 48: Temperature = 50.33°C
Step 49: Temperature = 49.73°C
Step 50: Temperature = 49.13°C
Step 51: Temperature = 48.55°C
Step 52: Temperature = 47.98°C
Step 53: Temperature = 47.42°C
Step 54: Temperature = 46.87°C
Step 55: Temperature = 46.33°C
Step 56: Temperature = 45.81°C
Step 57: Temperature = 45.29°C
Step 58: Temperature = 44.79°C
Step 59: Temperature = 44.29°C
Step 60: Temperature = 43.80°C
Step 61: Temperature = 43.33°C
Step 62: Temperature = 42.86°C
Step 63: Temperature = 42.40°C
Step 64: Temperature = 41.96°C
Step 65: Temperature = 41.52°C
Step 66: Temperature = 41.09°C
Step 67: Temperature = 40.67°C
Step 68: Temperature = 40.25°C
Step 69: Temperature = 39.85°C
Step 70: Temperature = 39.45°C
Step 71: Temperature = 39.06°C
Step 72: Temperature = 38.68°C
Step 73: Temperature = 38.31°C
Step 74: Temperature = 37.94°C
Step 75: Temperature = 37.58°C
Step 76: Temperature = 37.23°C
Step 77: Temperature = 36.88°C
Step 78: Temperature = 36.55°C
Step 79: Temperature = 36.22°C
Step 80: Temperature = 35.89°C
Step 81: Temperature = 35.57°C
Step 82: Temperature = 35.26°C
Step 83: Temperature = 34.96°C
Step 84: Temperature = 34.66°C
Step 85: Temperature = 34.37°C
Step 86: Temperature = 34.08°C
Step 87: Temperature = 33.80°C
Step 88: Temperature = 33.52°C
Step 89: Temperature = 33.25°C
Step 90: Temperature = 32.98°C
Step 91: Temperature = 32.73°C
Step 92: Temperature = 32.47°C
Step 93: Temperature = 32.22°C
Step 94: Temperature = 31.98°C
Step 95: Temperature = 31.74°C
Step 96: Temperature = 31.50°C
Step 97: Temperature = 31.27°C
Step 98: Temperature = 31.05°C
Step 99: Temperature = 30.83°C
Step 100: Temperature = 30.61°C

Iteration limit reached after 100 steps
Current temperature: 30.61°C (still 10.61°C above room temperature)

How this works:

1. The converged flag:

  • Initialised before the loop by checking if we’re already at equilibrium

  • This handles the edge case where temperature might already be at room temperature

  • Updated inside the loop when the threshold is reached

2. The while loop has two conditions connected by and:

  • not converged - continue if we haven’t reached equilibrium

  • iteration < max_iterations - but stop if we exceed the iteration limit

  • The loop continues only while both conditions are True

3. How the loop can exit:

  • Natural convergence: When converged = True is set inside the loop, the condition not converged becomes False, and the loop exits

  • Iteration limit: When iteration reaches max_iterations, the condition iteration < max_iterations becomes False, and the loop exits

4. After the loop:

  • The converged flag tells us definitively whether equilibrium was achieved

  • If converged = True: loop exited because threshold was met (success)

  • If converged = False: loop exited because iteration limit was reached

Alternative Solution 1: Using break#

An alternative approach uses break to exit the loop early when the iteration limit is reached. This separates the two exit conditions rather than combining them in the while statement.

temperature = 100.0  # Starting temperature in °C
room_temperature = 20.0  # Ambient temperature in °C
threshold = 0.5  # Stop when within 0.5°C of room temp
cooling_rate = 0.02  # Cool by 2% per step
iteration = 0
max_iterations = 100
# Assume we will converge. 
# Set to False inside the loop if we reach the break condition.
converged = True  

while temperature - room_temperature > threshold:
    # Cool by 2% of the temperature difference each step
    temperature_difference = temperature - room_temperature
    temperature = temperature - cooling_rate * temperature_difference
    iteration += 1
    print(f"Step {iteration}: Temperature = {temperature:.2f}°C")
    
    # Check if we've exceeded the iteration limit
    if iteration >= max_iterations:
        converged = False  # We didn't converge in time
        break  # Exit the loop early

# code for reporting the results is the same as before

Alternative Solution 2: Using a Function to Test Convergence#

In the main solution, we test the convergence condition twice:

  1. Before the loop: converged = temperature - room_temperature <= threshold

  2. Inside the loop: if temperature - room_temperature <= threshold:

When the same code appears multiple times, it’s good practice to define a function. This approach demonstrates the DRY principle (Don’t Repeat Yourself). In this case, it ensures that we always use the same convergence criteria. And if we want to change how convergence is defined, we only need to make these changes in one place.

def is_converged(temperature, room_temperature, threshold):
    """
    Check if the temperature has converged to within the threshold.
    
    Args:
        temperature (float): Current temperature (°C).
        room_temperature (float): Ambient temperature (°C).
        threshold (float): Convergence threshold (°C).
    
    Returns:
        bool: True if converged, False otherwise.
    """
    return temperature - room_temperature <= threshold


temperature = 100.0  # Starting temperature in °C
room_temperature = 20.0  # Ambient temperature in °C
threshold = 0.5  # Stop when within 0.5°C of room temp
cooling_rate = 0.02  # Cool by 2% per step
iteration = 0
max_iterations = 100

# Check if we're already at equilibrium before starting the loop
converged = is_converged(temperature, room_temperature, threshold)

while not converged and iteration < max_iterations:
    # Cool by 2% of the temperature difference each step
    temperature_difference = temperature - room_temperature
    temperature = temperature - cooling_rate * temperature_difference
    iteration += 1
    print(f"Step {iteration}: Temperature = {temperature:.2f}°C")
    
    # Check if we've reached equilibrium using the same convergence testing function
    converged = is_converged(temperature, room_temperature, threshold)

# code for reporting the results is the same as before

Why use a function for the convergence test?

1. DRY Principle (Don’t Repeat Yourself):

  • The convergence condition temperature - room_temperature <= threshold was written twice in the original code

  • With a function, we define the logic once and reuse it

  • This reduces code duplication

2. Easier to modify:

  • If we want to change the convergence criterion (e.g., use absolute value, or a percentage-based threshold), we only need to update it in one place

  • Without a function, we’d need to find and update every occurrence

3. More readable:

  • is_converged(temperature, room_temperature, threshold) reads naturally - it’s clear what we’re checking

  • The calculation temperature - room_temperature <= threshold requires mental parsing to understand

4. Easier to test:

  • We can test the convergence function independently with different inputs

  • This is especially valuable for more complex convergence criteria

5. Reusability:

  • If we have multiple simulations with the same convergence criterion, we can reuse the function

  • Promotes consistent behaviour across different parts of the code

Example of when this matters:

Suppose we later realise the convergence criterion should check the absolute temperature difference (to handle cooling and heating):

def is_converged(temperature, room_temperature, threshold):
    """Check if temperature has converged to within threshold."""
    return abs(temperature - room_temperature) <= threshold

With a function, this is a one-line change. Without it, we’d need to find and update both occurrences, risking bugs if we miss one.

Trade-off: For very simple conditions used only 2-3 times in short code, a function might be overkill. But as code grows in complexity, extracting repeated logic into functions becomes increasingly valuable.