ENCM-100 · Lesson 3 of 5 · 35 min
Decisions and loops
The programs in Lesson 2 ran straight through from top to bottom, doing the same thing every time. Real engineering programs need to make decisions (is this member overstressed? is the flow laminar?) and to repeat work (check every reading, try every section size, iterate until an answer converges). Decisions and loops are what make a program more than a calculator.
This chapter covers comparisons and logical operators, if statements, while loops and for loops, the accumulator pattern for totals and maximums, break and continue, and input validation. It finishes with two workhorse numerical methods built from loops, bisection and Newton's method, and a brute-force design search. These are the patterns behind a large share of engineering software.
Comparisons and Boolean logic
A comparison produces a bool, True or False:
- == equal, != not equal, < less than, <= less than or equal, > greater than, >= greater than or equal.
Remember: one = assigns, two == compares. And from Lesson 2, never compare floats with == (use math.isclose).
Comparisons combine with the logical operators and, or and not. Python also allows chained comparisons that read like mathematics: 0 <= angle < 360.
stress_MPa = 159.2
allowable_MPa = 165
temperature_C = -38
print(stress_MPa <= allowable_MPa)
print(stress_MPa <= allowable_MPa and temperature_C >= -40)
print(not (temperature_C < -30))
print(0 <= 45 < 90)True
True
False
Trueif, elif and else
An if statement runs a block of code only when a condition is True. The block is marked by indentation, four spaces by convention, and the line before it ends with a colon. Indentation is not decoration in Python; it is how the language knows which lines belong to the block.
stress_MPa = 172.0
allowable_MPa = 165
if stress_MPa <= allowable_MPa:
print("OK")
else:
overstress = (stress_MPa / allowable_MPa - 1) * 100
print(f"Overstressed by {overstress:.1f}%")Output: Overstressed by 4.2%.
For more than two cases, add elif ("else if") branches. Python checks the conditions in order and runs only the first block whose condition is True.
Worked problem
Classifying pipe flow
Pipe flow is commonly treated as laminar for Re below about 2300, turbulent above about 4000, and transitional in between. Write code that classifies the flow from Lesson 2 (Re = 179 640).
Show solution
rho, V, D, mu = 998, 1.8, 0.100, 1.0e-3 Re = rho * V * D / mu if Re < 2300: regime = "laminar" elif Re <= 4000: regime = "transitional" else: regime = "turbulent" print(f"Re = {Re:,.0f}: {regime}")- Output: Re = 179,640: turbulent.
- Because elif is only checked when the first test fails, the second condition does not need to repeat "Re >= 2300".
Answer: Turbulent. Test the boundaries too: Re = 2300 should give transitional, and Re = 4000 transitional, matching the code's < and <= choices
Loops: repeating work
Python has two kinds of loop. A while loop repeats as long as a condition stays True; use it when you do not know in advance how many repetitions are needed. A for loop repeats once for each item in a sequence; use it when you do.
while loops
Worked problem
How long until demand doubles?
Water demand in a growing town rises 3% per year. How many years until it doubles?
Show solution
years = 0 demand = 1.0 # relative to today while demand < 2.0: demand = demand * 1.03 years = years + 1 print(years, round(demand, 3))- Output: 24 2.033.
- Check: \(1.03^{24} = 2.033\) and \(1.03^{23} = 1.974\), so 24 years is correct. (The "rule of 72" estimates 72/3 = 24.)
Answer: 24 years. The loop body must change something that the condition depends on; otherwise the condition never becomes False
That last point is important. If the body never changes the condition (say, you forgot to update demand), the loop runs forever. If a program seems to hang, an infinite loop is the first suspect; stop it with Ctrl+C.
for loops and range
A for loop takes each item of a sequence in turn. The built-in range() produces sequences of integers: range(5) gives 0, 1, 2, 3, 4 (five values, starting at 0, stopping before 5); range(10, 51, 10) gives 10, 20, 30, 40, 50.
Worked problem
A load table
Print the axial stress in a 20 mm diameter rod for loads from 10 to 50 kN in steps of 10 kN.
Show solution
import math area_mm2 = math.pi * 20 ** 2 / 4 print("Load (kN) Stress (MPa)") for load_kN in range(10, 51, 10): stress = load_kN * 1000 / area_mm2 print(f"{load_kN:>9} {stress:>12.1f}")- Output:
Load (kN) Stress (MPa) 10 31.8 20 63.7 30 95.5 40 127.3 50 159.2
Answer: Five rows. Note range(10, 51, 10): the stop value 51 is excluded, so 50 is the last value; range(10, 50, 10) would stop at 40
A for loop can also go straight through the items of a list, a collection written in square brackets. Lists get a full treatment in Lesson 5; for now, readings = [612, 655, 701] is a list of three numbers, and for r in readings: visits each one.
The accumulator pattern
Many loops build up a result as they go: a running total, a count, a maximum. Set the result to a starting value before the loop and update it inside. This is the pseudocode from Lesson 1, turned into Python.
Worked problem
Strain gauge summary
Ten hourly strain readings (microstrain) are 612, 655, 701, 748, 790, 823, 845, 812, 776 and 731. Find the average, the maximum and how many exceed 800.
Show solution
readings = [612, 655, 701, 748, 790, 823, 845, 812, 776, 731] LIMIT = 800 total = 0 largest = readings[0] count_over = 0 for r in readings: total = total + r if r > largest: largest = r if r > LIMIT: count_over = count_over + 1 average = total / len(readings) print(f"Average {average:.1f}, maximum {largest}, {count_over} above {LIMIT}")- Output: Average 749.3, maximum 845, 3 above 800.
Answer: Average 749.3 microstrain, maximum 845, three readings above the limit. Python has built-ins sum(), max() and len() that do some of this in one line, but the pattern itself is universal and works for any condition you need
Starting largest at the first reading, not at 0, matters: if all readings were negative (compressive strain), starting at 0 would wrongly report 0 as the maximum.
break and continue
break leaves the loop immediately; continue skips the rest of the current pass and moves to the next item. Use them sparingly; they are clearest for "stop at the first match" and "skip bad data".
readings = [612, 655, -999, 748, 790, 823, 845] # -999 marks a dropped reading
for hour, r in enumerate(readings):
if r == -999:
continue # skip missing data
if r > 800:
print(f"First exceedance at hour {hour}: {r}")
breakOutput: First exceedance at hour 5: 823. The built-in enumerate() supplies a counter (starting at 0) alongside each item.
Validating input
Programs that take input from people or files must expect bad input. A while loop that repeats until the input is acceptable, combined with try and except to catch conversion errors, is the standard pattern.
while True:
text = input("Span in metres (positive): ")
try:
span_m = float(text)
except ValueError:
print("That is not a number. Try again.")
continue
if span_m > 0:
break
print("The span must be positive.")
print(f"Span accepted: {span_m} m")while True: creates a loop that only ends at a break. The try block attempts the conversion; if float() raises a ValueError, the except block runs instead of crashing the program.
Numerical methods built from loops
Many engineering equations cannot be solved with algebra: friction factors, beam natural frequencies, chemical equilibria, the depth of flow in a channel. Loops let a program close in on the answer numerically.
Bisection
If a continuous function changes sign between two points, it has a root between them (the intermediate value theorem from MATH-100). Bisection halves the interval repeatedly, always keeping the half where the sign change is.
Worked problem
Solving a cubic by bisection
Solve \(x^3 - 2x - 5 = 0\) on [2, 3] to within \(10^{-6}\).
Show solution
- Check the bracket: \(f(2) = -1\) and \(f(3) = 16\), so there is a sign change.
lo, hi = 2.0, 3.0 steps = 0 while hi - lo > 1e-6: mid = (lo + hi) / 2 f_lo = lo ** 3 - 2 * lo - 5 f_mid = mid ** 3 - 2 * mid - 5 if f_lo * f_mid <= 0: # sign change in the left half hi = mid else: # otherwise it is in the right half lo = mid steps = steps + 1 print(steps, (lo + hi) / 2)- Output: 20 2.0945515632629395.
Answer: x ≈ 2.094552 after 20 halvings. Each step halves the interval, so reaching 10⁻⁶ from a width of 1 takes log₂(10⁶) ≈ 20 steps. Bisection is slow but cannot fail once the root is bracketed
Newton's method
Newton's method (MATH-100, Lesson 4) uses the derivative to jump toward the root: \(x_{n+1} = x_n - f(x_n)/f'(x_n)\). It converges much faster when it works, roughly doubling the number of correct digits each step, but it needs the derivative and a reasonable starting guess.
x = 2.0
for n in range(1, 6):
x = x - (x ** 3 - 2 * x - 5) / (3 * x ** 2 - 2)
print(n, x)1 2.1
2 2.094568121104185
3 2.094551481698199
4 2.0945514815423265
5 2.0945514815423265Four steps reach full machine precision, against bisection's 20 steps for six digits. Real programs usually stop when the change between steps falls below a tolerance, with a maximum number of iterations as a safety net in case the method fails to converge.
Nested loops: a brute-force design search
A loop inside a loop runs the inner loop completely for each pass of the outer one. With computers this fast, simply trying every combination is often the easiest way to find a good design.
Worked problem
Choosing a timber beam size
A rectangular timber beam must carry M = 45 kN·m with bending stress \(\sigma = 6M/(bh^2)\) no more than 10 MPa. Widths b from 100 to 300 mm and depths h from 200 to 600 mm are available in 50 mm steps, and for stability the depth may not exceed 3 times the width. Find the allowed section with the smallest cross-sectional area.
Show solution
M = 45e6 # N.mm best = None for b in range(100, 301, 50): for h in range(200, 601, 50): stress = 6 * M / (b * h ** 2) if stress <= 10 and h <= 3 * b: area = b * h if best is None or area < best[0]: best = (area, b, h, stress) print(best)- Output: (67500, 150, 450, 8.88888888888889).
- The search checks 5 × 9 = 45 combinations. None is a special value meaning "nothing yet", so the first valid section always becomes the best so far.
Answer: 150 × 450 mm, area 67 500 mm², stress 8.9 MPa. Without the stability limit, the search would pick a slender 100 × 550 mm section; constraints matter as much as the objective
Brute force has limits: a search over ten parameters with ten values each needs ten billion evaluations. For larger problems, engineers use optimization methods that search intelligently. But for a few parameters, trying everything is quick, transparent and hard to get wrong.
Common mistakes
MistakeUsing = instead of == in a condition.
Instead= assigns; == compares. Python reports if x = 5: as a syntax error.
MistakeInconsistent indentation, or forgetting the colon after if, for or while.
InsteadEnd the line with a colon and indent the block by four spaces, consistently.
MistakeA while loop whose body never changes the condition.
InsteadMake sure each pass moves toward ending the loop; add a maximum iteration count for numerical methods.
MistakeExpecting range(1, 10) to include 10.
Insteadrange stops before the stop value; use range(1, 11) for 1 to 10.
MistakeStarting a running maximum at 0.
InsteadStart it at the first item, so negative data are handled correctly.
MistakeOrdering elif conditions so an earlier branch catches cases meant for a later one.
InsteadOrder conditions from most specific (or smallest range) and test the boundary values.
Summary and key terms
- Comparisons (==, !=, <, <=, >, >=) produce True or False; combine them with and, or, not.
- if/elif/else chooses one block to run; indentation and colons define the blocks.
- while loops repeat while a condition holds; for loops visit each item of a sequence such as range() or a list.
- The accumulator pattern builds totals, counts and maximums; start maximums at the first item.
- break exits a loop; continue skips to the next pass; try/except and while True validate input.
- Bisection halves a bracketing interval reliably; Newton's method converges much faster but needs a derivative and a good start.
- Nested loops can search all combinations of design parameters subject to constraints.
- Boolean expression
- An expression that evaluates to True or False.
- Conditional statement
- An if, elif or else block that runs only when its condition holds.
- Block
- A group of indented lines that belong to a statement.
- while loop
- A loop that repeats while its condition is True.
- for loop
- A loop that runs once for each item in a sequence.
- range
- A built-in that produces a sequence of integers.
- Accumulator
- A variable updated in a loop to build up a total, count or extreme value.
- Infinite loop
- A loop whose condition never becomes False.
- Exception handling
- Using try and except to respond to runtime errors instead of crashing.
- Bisection
- A root-finding method that repeatedly halves an interval containing a sign change.
- Convergence
- Successive approximations approaching a fixed answer.
- Brute-force search
- Finding the best option by evaluating every combination.
Practice problems
Work each problem on paper first, then reveal the answer to check.
What does print(5 > 3 and 2 > 4 or not False) display?
Show answer
True. and binds tighter than or: (True and False) or True = False or True = True.
Write code that prints "PASS" if a measured deflection d (mm) is at most span_mm / 360, and "FAIL" otherwise.
Show answer
if d <= span_mm / 360: print("PASS") else: print("FAIL") (with the colon, newline and indentation).
List the values produced by range(3), range(2, 8, 2) and range(10, 0, -3).
Show answer
0, 1, 2; 2, 4, 6; 10, 7, 4, 1.
Write a for loop that adds the integers 1 to 100 and prints the total.
Show answer
total = 0; for k in range(1, 101): total = total + k; print(total) gives 5050.
A bank of 40 batteries loses 2% of its capacity per year. Write a while loop to find when capacity first falls below 80%.
Show answer
cap = 1.0; years = 0; while cap >= 0.8: cap *= 0.98; years += 1. Result: 12 years (0.98¹¹ = 0.801, 0.98¹² = 0.785).
What is wrong with: n = 10; while n > 0: print(n)?
Show answer
n never changes, so the loop never ends. Add n = n - 1 inside the loop.
Modify the strain gauge example to report the hour (index) of the maximum reading.
Show answer
Track the index: for i, r in enumerate(readings): if r > largest: largest = r; hour_max = i. Result: hour 6.
How many bisection steps are needed to shrink an interval of width 4 to below 0.001?
Show answer
4/2ⁿ < 0.001 → 2ⁿ > 4000 → n ≥ 12 (2¹² = 4096).
Use Newton's method by hand for √10 (solve x² − 10 = 0) starting at x = 3. Give two iterations.
Show answer
x₁ = 3 − (9 − 10)/6 = 3.1667; x₂ = 3.1667 − (10.0278 − 10)/6.3333 = 3.16228.
In the beam search, what section is chosen if the allowable stress is raised to 12 MPa (keeping h ≤ 3b)?
Show answer
Need bh² ≥ 2.25 × 10⁷. 100 × 300 gives 9 × 10⁶ (no); 150 × 400 gives 2.4 × 10⁷ (yes, area 60 000); no smaller area works (e.g. 100 × 300 fails, 150 × 350 gives 1.84 × 10⁷). Answer 150 × 400 mm.
Lesson quiz · 5 questions
Score 60% or more to complete this lesson.
Create a free account to save your progress, take quizzes and earn a certificate.