Your First Calculations in Python¶
Time required: 20-25 minutes
Prerequisites: Module 2b (uv installed, Python environment ready)
What you’ll learn:
Understand notebook cells (markdown vs. code)
Work with variables and basic calculations
Use imports to access additional functionality
Create your first hydraulic function
Why this matters: Before we dive into real streamflow data analysis, you need to understand how Python notebooks work and the fundamental building blocks of Python code. This module gives you those foundations through practical hydraulic engineering examples.
Coming from Excel? You’re Already Halfway There!¶
If you’ve been using Excel for water modelling calculations, you already understand the core concepts—you just need to learn new syntax. Here’s the good news: everything you do in Excel has a Python equivalent, and Python makes it easier to repeat, share, and scale your work.
Excel → Python Translation¶
| What You Do in Excel | Python Equivalent |
|---|---|
Cell with a number: 15.5 | Variable: Q = 15.5 |
Formula: =A1 + B1 | Expression: a + b |
=SUM(A:A) | df['A'].sum() |
=AVERAGE(A:A) | df['A'].mean() |
=MAX(A:A) | df['A'].max() |
| Filter rows by value | df[df['Q'] > 10] |
| VLOOKUP | df.merge() or df.join() |
| Pivot Table | df.groupby().agg() |
| Chart | matplotlib plotting |
Don’t worry if the Python column looks unfamiliar—by the end of Module 4, you’ll understand all of these!
Why Make the Switch?¶
| Excel Limitation | Python Solution |
|---|---|
| Manual repetition (copy formulas for each station) | Write once, apply to 100 stations automatically |
| “Which version is correct?” (v2_final_FINAL.xlsx) | Version control tracks every change |
| Hard to share methods | Code documents exactly what you did |
| Breaks with large datasets | Handles millions of rows efficiently |
| Formula errors hide in cells | Errors are explicit and traceable |
The Mindset Shift¶
In Excel: You work interactively, clicking and dragging, seeing results immediately.
In Python: You write instructions (code) that the computer follows. This feels slower at first, but:
You can re-run the same analysis on new data instantly
Your colleague can see exactly what you did
You can automate weekly/monthly reports
Results are reproducible years later
💡 Think of Python code as a recipe: Excel is like cooking by taste—you know it works but can’t easily explain how. Python is like a detailed recipe—anyone can follow it and get the same result.
Now let’s learn the Python building blocks that make this possible!
Understanding Jupyter Notebook Cells¶
A Jupyter notebook is made up of cells. There are two main types you’ll use:
1. Markdown Cells (Text)¶
These cells contain formatted text, like this one you’re reading now. They’re perfect for:
Explanations and documentation
Headings and structure
Equations written in LaTeX
Images and links
This cell is a markdown cell. Notice it displays formatted text, not code.
2. Code Cells (Python)¶
These cells contain executable Python code. When you run them, Python executes the code and shows any results below the cell.
First time opening a notebook?
When you first open a notebook in VS Code, you’ll see a ▶️ Run button next to code cells and not yet this [ ]. Don’t worry - this is normal! The first time you try to run a code cell, VS Code will ask you to select a Python interpreter.
Selecting the Python Interpreter¶
When you click the Run button for the first time, you’ll see a dropdown menu asking you to “Select Kernel” or “Select Python Interpreter”. Here’s what to do:
Look for your project’s uv environment - It should be listed as something like:
Python 3.x.x ('.venv': venv)Or it might show the full path:
.venv/bin/python
If you don’t see it, click “Python Environments...” and select:
The
.venvfolder in your project directoryOr run
uv syncin the terminal first to create the environment
After selecting, the notebook will connect to your Python environment and you’ll see
[ ]:appear next to code cells
💡 Why does this matter? Selecting the right interpreter ensures your notebook uses the Python packages you installed with
uv(likepandas,matplotlib, etc.) in your project’s virtual environment.
What You’ll See¶
Once connected to the Python interpreter:
Code cells show
[ ]:on the left before you run themAfter running, they show
[1]:(or another number) indicating execution orderThey may display output below
Try It: Your First Code Cell¶
The cell below is a code cell. Let’s run it!
To run a code cell:
Click on the cell to select it
Press
Shift + Enter(or click the ▶️ play button on the left)
# This is a code cell!
# Lines starting with # are comments - Python ignores them
# They're for humans to read
print("Hello, Water Modeller!")What happened?
The
print()function displayed textThe
#symbol marks comments (notes for humans, ignored by Python)The cell executed and showed output below
Important: Run cells in order! Notebooks are meant to be run from top to bottom. Later cells often depend on variables or imports from earlier cells. If you skip a cell or run them out of order, you may get errors like
NameError: name 'variable' is not defined.If things stop working, try: Kernel → Restart and then run all cells from the beginning.
Variables: Storing Values¶
In hydraulic calculations, we work with many values: discharge, velocity, area, slope. Variables let us store these values and reuse them.
Think of a variable as a labeled container:
Q = 15.5 # Discharge in m³/sHere, Q is the variable name, and 15.5 is the value stored inside it.
Example: Manning’s Formula Calculation¶
Let’s calculate flow velocity using a simplified version of Manning’s equation. First, we’ll store our input values in variables:
# Channel properties
hydraulic_radius = 2.5 # meters
slope = 0.001 # m/m (0.1% slope)
mannings_n = 0.035 # roughness coefficient for natural channel
print("Hydraulic radius:", hydraulic_radius, "m")
print("Channel slope:", slope, "m/m")
print("Manning's n:", mannings_n)Notice:
Variable names describe what they contain (
hydraulic_radius, not justR)We can use underscores to make names readable
Numbers with decimals use a period (
.) not a commaComments explain the units (crucial in engineering!)
Performing Calculations¶
Now we can use these variables in calculations. Python uses standard math operators:
| Operation | Operator | Example |
|---|---|---|
| Addition | + | a + b |
| Subtraction | - | a - b |
| Multiplication | * | a * b |
| Division | / | a / b |
| Exponentiation | ** | a ** 2 (a squared) |
Let’s calculate velocity using Manning’s formula:
# Manning's equation for velocity
velocity = (1 / mannings_n) * (hydraulic_radius ** (2/3)) * (slope ** 0.5)
print("Flow velocity:", velocity, "m/s")What happened?
Python calculated the result using our variables
The
**operator raises a number to a power (e.g.,hydraulic_radius ** (2/3))We stored the result in a new variable called
velocityprint()displayed the result
💡 Key insight: Variables let us change inputs and recalculate easily. Try changing
slopeabove and re-running both cells!
Imports: Using Additional Tools¶
Python comes with basic math operations, but for engineering work we need more powerful tools. That’s where imports come in.
An import statement loads additional functionality from Python libraries (also called packages or modules).
Think of it like opening a toolbox:
import math # Opens the 'math' toolboxNow we can use tools from that toolbox:
math.sqrt(25) # Use the square root tool: √25 = 5Example: Calculating Pipe Flow¶
Let’s calculate flow rate in a circular pipe using the continuity equation (Q = A × v). We’ll need the math module for π and square root:
# Import the math module
import math
# Pipe properties
diameter = 0.5 # meters
velocity = 1.2 # m/s
# Calculate cross-sectional area (A = π r²)
radius = diameter / 2
area = math.pi * radius ** 2
# Calculate discharge (Q = A × v)
discharge = area * velocity
print(f"Pipe diameter: {diameter} m")
print(f"Flow velocity: {velocity} m/s")
print(f"Cross-sectional area: {area:.4f} m²")
print(f"Discharge: {discharge:.4f} m³/s")New concepts here:
import math: Loads the math modulemath.pi: Uses the constant π from the math module (≈3.14159)f"...{variable}...": Format strings (f-strings) that insert variable values into text{area:.4f}: Formats numbers to 4 decimal places
Common imports you’ll use in water modeling:
import math- Mathematical functions and constantsimport numpy- Numerical computing (arrays, advanced math)import pandas- Working with data tablesimport matplotlib.pyplot- Creating plots and graphs
Functions: Packaging Reusable Calculations¶
Imagine you need to calculate discharge for 50 different pipes. Copying the same calculation code 50 times would be tedious and error-prone.
Functions solve this problem. A function is a reusable block of code that:
Takes inputs (called parameters or arguments)
Performs calculations
Returns results
Anatomy of a Function¶
def function_name(parameter1, parameter2):
"""This is a docstring - it explains what the function does"""
# calculations go here
result = parameter1 + parameter2
return resultdef: Keyword that starts a function definitionfunction_name: Choose a descriptive name(parameter1, parameter2): Inputs the function needs""" docstring """: Documentation explaining the functionreturn: Sends the result back to whoever called the function
Example: Discharge Calculation Function¶
Let’s create a function to calculate discharge in a circular pipe:
import math
def calculate_pipe_discharge(diameter, velocity):
"""
Calculate discharge in a circular pipe.
Parameters:
-----------
diameter : float
Pipe diameter in meters
velocity : float
Average flow velocity in m/s
Returns:
--------
float
Discharge in m³/s
"""
# Calculate cross-sectional area
radius = diameter / 2
area = math.pi * radius ** 2
# Calculate discharge
discharge = area * velocity
return dischargeNow we can use this function for any pipe! Let’s calculate discharge for several different scenarios:
# Small pipe, low velocity
Q1 = calculate_pipe_discharge(diameter=0.3, velocity=0.8)
print(f"Scenario 1: Q = {Q1:.4f} m³/s")
# Medium pipe, moderate velocity
Q2 = calculate_pipe_discharge(diameter=0.6, velocity=1.5)
print(f"Scenario 2: Q = {Q2:.4f} m³/s")
# Large pipe, high velocity
Q3 = calculate_pipe_discharge(diameter=1.2, velocity=2.0)
print(f"Scenario 3: Q = {Q3:.4f} m³/s")Benefits of functions:
Reusability: Write once, use many times
Clarity: The function name describes what it does
Maintenance: Fix a bug in one place, not everywhere
Documentation: The docstring explains inputs, outputs, and units
💡 Best practice for engineering: Always document units in your docstrings!
Example: Critical Depth in a Rectangular Channel¶
Let’s create a function to calculate critical depth in a rectangular channel.
The formula is:
Where:
= critical depth (m)
= discharge per unit width (m²/s)
= gravitational acceleration (9.81 m/s²)
import math
def calculate_critical_depth(discharge_per_width):
"""
Calculate critical depth in a rectangular channel.
Parameters:
-----------
discharge_per_width : float
Discharge per unit width (q) in m²/s
Returns:
--------
float
Critical depth in meters
"""
g = 9.81 # gravitational acceleration in m/s²
# Calculate critical depth: yc = (q²/g)^(1/3)
critical_depth = (discharge_per_width ** 2 / g) ** (1/3)
return critical_depth
# Test the function
q = 2.5 # m²/s
yc = calculate_critical_depth(q)
print(f"For q = {q} m²/s, critical depth = {yc:.3f} m")Result: For q = 2.5 m²/s, the critical depth is approximately 0.860 m.
💡 Try it yourself: Change the value of q in the cell above and re-run it to see how critical depth changes with different discharge rates!
Summary: What You’ve Learned¶
Congratulations! You now understand the fundamental building blocks of Python programming:
1. Notebook Cells¶
Markdown cells: Formatted text, documentation, equations
Code cells: Executable Python code
Run cells with
Shift + Enter
2. Variables¶
discharge = 15.5 # Store values in named containers3. Calculations¶
velocity = discharge / area # Use standard operators: +, -, *, /, **4. Imports¶
import math # Load additional functionality
value = math.sqrt(25) # Use functions from imported modules5. Functions¶
def calculate_something(input1, input2):
"""Documentation here"""
result = input1 + input2
return resultKey Principles for Engineering Code:¶
✅ Use descriptive variable names
✅ Always document units in comments and docstrings
✅ Write reusable functions for repeated calculations
✅ Test your code with known values
What’s Next?¶
Now that you understand these Python basics, you’re ready to:
Module 3b: AI-Assisted Coding
Learn to use AI assistants (ChatGPT, Claude) effectively for coding
Write better prompts to get working code
Understand AI limitations for engineering work
You’ll see these concepts everywhere:
Variables storing discharge timeseries
Imports bringing in
pandasandmatplotlibFunctions processing hydrological data
The complexity increases, but the fundamentals stay the same!
Optional Challenge: Froude Number¶
If you want more practice, try creating a function to calculate the Froude number:
Where:
= Froude number (dimensionless)
= flow velocity (m/s)
= 9.81 m/s²
= flow depth (m)
Bonus: Add logic to determine if the flow is subcritical (Fr < 1), critical (Fr = 1), or supercritical (Fr > 1).
# Your solution here:
import math
def calculate_froude_number(velocity, depth):
"""
Calculate Froude number for open channel flow.
Parameters:
-----------
velocity : float
Flow velocity in m/s
depth : float
Flow depth in m
Returns:
--------
float
Froude number (dimensionless)
"""
g = 9.81 # m/s²
# TODO: Calculate Froude number using the formula: Fr = v / sqrt(g * y)
# Your code here
return 0.0 # Placeholder - replace with your calculation
# Test your function:
# Fr = calculate_froude_number(velocity=2.0, depth=1.5)
# print(f"Froude number: {Fr:.3f}")
# BONUS: Add if/elif/else logic to determine flow regime
# if Fr < 1:
# print("Flow regime: Subcritical")
# elif Fr > 1:
# print("Flow regime: Supercritical")
# else:
# print("Flow regime: Critical")Click to reveal solution
import math
def calculate_froude_number(velocity, depth):
"""
Calculate Froude number for open channel flow.
Parameters:
-----------
velocity : float
Flow velocity in m/s
depth : float
Flow depth in m
Returns:
--------
tuple
(Froude number, flow regime string)
"""
g = 9.81 # m/s²
# Calculate Froude number: Fr = v / sqrt(g * y)
froude = velocity / math.sqrt(g * depth)
# Determine flow regime
if froude < 1:
regime = "Subcritical"
elif froude > 1:
regime = "Supercritical"
else:
regime = "Critical"
return froude, regime
# Test the function
Fr, regime = calculate_froude_number(velocity=2.0, depth=1.5)
print(f"Froude number: {Fr:.3f}")
print(f"Flow regime: {regime}")
# Test with subcritical flow
Fr2, regime2 = calculate_froude_number(velocity=0.5, depth=2.0)
print(f"\nSubcritical test: Fr = {Fr2:.3f} ({regime2})")
# Test with supercritical flow
Fr3, regime3 = calculate_froude_number(velocity=5.0, depth=0.3)
print(f"Supercritical test: Fr = {Fr3:.3f} ({regime3})")Expected output:
Froude number: 0.521
Flow regime: Subcritical
Subcritical test: Fr = 0.113 (Subcritical)
Supercritical test: Fr = 2.914 (Supercritical)Next Module: Module 3b: AI-Assisted Coding
Learn how to use AI assistants to help you write Python code more effectively!