Chapter 1: Basic Python Math#
1.1 Basic Math Operation#
Python can be used as a powerful calculator! Think of it like a super-charged graphing calculator that can handle both simple arithmetic and complex mathematical operations.
In Jupyter notebooks, you simply type a mathematical expression in a code cell, press Shift+Enter, and the result appears right below. Let’s explore the basic mathematical operators that Python provides:
1.1.1 Basic Python Math Operators#
Operator |
Description |
Example |
Result |
|---|---|---|---|
|
Addition |
|
|
|
Subtraction |
|
|
|
Multiplication |
|
|
|
Division (floating point) |
|
|
|
Floor division (integer) |
|
|
|
Modulus (remainder) |
|
|
|
Exponentiation (power) |
|
|
|
Parentheses (grouping) |
|
|
Let’s try some of these operators with real examples:
Python uses the standard math operators for addition, subtraction, multiplication, and division, and follows the usual order of operations. You can use parentheses to control how calculations are evaluated. Python ignores spaces within a line (so you can format expressions for readability), but it does care about spaces at the beginning of a line—an important concept we’ll discuss later when we cover conditions and loops.
# Basic arithmetic
print(f"5 + 3 = {5 + 3}")
print(f"5 * 3 = {5 * 3}")
print(f"5 / 3 = {5 / 3}")
print(f"5 ** 3 (5 to the power of 3) = {5 ** 3}")
5 + 3 = 8
5 * 3 = 15
5 / 3 = 1.6666666666666667
5 ** 3 (5 to the power of 3) = 125
Two operators that often confuse beginners are floor division and modulo. Let’s understand what they do:
Floor Division (//):
Returns the whole number part of division (rounds down to nearest integer)
Example:
7 // 2 = 3(not 3.5)Useful when you need to know “how many complete groups” you can make
Modulo (%):
Returns the remainder after division
Example:
7 % 2 = 1(because 7 ÷ 2 = 3 remainder 1)Useful for finding what’s “left over” after dividing
print(f"25 // 7 = {25 // 7}")
print(f"25 % 7 = {25 % 7}")
25 // 7 = 3
25 % 7 = 4
1.2 Integers & Floats#
What Are Data Types?
A data type tells Python what kind of information a value represents and what operations can be performed on it. Think of it like categories that help Python understand how to handle different kinds of data. Just like you wouldn’t try to multiply a word by a number in real life, Python uses types to keep track of what makes sense to do with each piece of data.
1.2.1 Two Main Number Types in Python#
Python has two primary types for numbers:
Integers (
int)
Whole numbers with no decimal point
Examples:
2,53,-10,0Used for counting, indexing, exact quantities
Perfect for things like: number of reactors, batch count, sample size
Floats (
float)
Numbers with decimal points (even if the decimal part is zero)
Examples:
3.0,1.2,-5.7,2.5e-3(scientific notation)Used for measurements, calculations, continuous values
Perfect for things like: temperature, pressure, concentration, flow rates
How Python Decides the Result Type
Python follows simple rules when doing math:
All integers + whole number result → Integer
5 + 3 = 8(integer)10 * 4 = 40(integer)
Any float involved → Float
5.0 + 3 = 8.0(float, because 5.0 is a float)2 * 3.5 = 7.0(float, because 3.5 is a float)
Division always produces floats (even with integers)
6 / 2 = 3.0(float, even though result is whole)7 / 2 = 3.5(float)
# Integers vs Floats Examples
print("=== Integer Examples ===")
batch_count = 5 # Integer: whole number of batches
reactor_num = 2 # Integer: reactor identifier
print(f"batch_count = {batch_count} (type: {type(batch_count).__name__})")
print(f"reactor_num = {reactor_num} (type: {type(reactor_num).__name__})")
print("\n=== Float Examples ===")
temperature = 85.7 # Float: temperature measurement
pressure = 2.0 # Float: even though it's whole, it has decimal
flow_rate = 1.25e2 # Float: scientific notation = 125.0
print(f"temperature = {temperature} (type: {type(temperature).__name__})")
print(f"pressure = {pressure} (type: {type(pressure).__name__})")
print(f"flow_rate = {flow_rate} (type: {type(flow_rate).__name__})")
print("\n=== How Python Decides Result Types ===")
# Integer operations
result1 = 10 + 5 # Both integers → integer result
print(f"10 + 5 = {result1} (type: {type(result1).__name__})")
# Mixed operations (float involved)
result2 = 10 + 5.0 # One float → float result
print(f"10 + 5.0 = {result2} (type: {type(result2).__name__})")
result3 = 2 * 3.5 # One float → float result
print(f"2 * 3.5 = {result3} (type: {type(result3).__name__})")
# Division always gives float
result4 = 6 / 2 # Division → always float
print(f"6 / 2 = {result4} (type: {type(result4).__name__})")
result5 = 7 / 2 # Division → always float
print(f"7 / 2 = {result5} (type: {type(result5).__name__})")
# Floor division gives integer if inputs are integers
result6 = 7 // 2 # Floor division with integers → integer
print(f"7 // 2 = {result6} (type: {type(result6).__name__})")
=== Integer Examples ===
batch_count = 5 (type: int)
reactor_num = 2 (type: int)
=== Float Examples ===
temperature = 85.7 (type: float)
pressure = 2.0 (type: float)
flow_rate = 125.0 (type: float)
=== How Python Decides Result Types ===
10 + 5 = 15 (type: int)
10 + 5.0 = 15.0 (type: float)
2 * 3.5 = 7.0 (type: float)
6 / 2 = 3.0 (type: float)
7 / 2 = 3.5 (type: float)
7 // 2 = 3 (type: int)
Why Data Types Matter
In Python (and scientific computing), the data type you choose affects:
How much memory your data uses
How precise your numerical calculations are
Choosing the right data type helps you balance accuracy, performance, and memory efficiency, especially for large datasets or simulations.
Example: float32 vs. float64
Two common numeric types are:
float32— 32-bit floating-point numberfloat64— 64-bit floating-point number (NumPy’s default)
Memory Usage
Each type uses a fixed amount of memory per number:
Data Type |
Size per Value |
Memory for 1 Million Values |
|---|---|---|
|
4 bytes |
~4 MB |
|
8 bytes |
~8 MB |
float64 uses twice the memory of float32. For large simulations or datasets, this difference becomes significant.
Accuracy
float64 stores more bits, giving more precise numerical results.
float32precision: ~7 decimal digitsfloat64precision: ~15–16 decimal digits
# Why do float32 and float64 give different results?
import numpy as np
x32 = np.float32(0.1)
x64 = np.float64(0.1)
print("=== The Results ===")
print(f"float32: {x32} × 3 = {x32 * 3}")
print(f"float64: {x64} × 3 = {x64 * 3}")
print("\n=== Why Are They Different? ===")
print("The issue is that 0.1 cannot be represented exactly in binary!")
print("Just like 1/3 = 0.333... in decimal (goes on forever),")
print("0.1 in binary is 0.000110011001100... (repeating pattern)")
print(f"\n=== How Each Type Stores 0.1 ===")
print(f"float32 stores 0.1 as: {x32:.17f}")
print(f"float64 stores 0.1 as: {x64:.17f}")
print(f"True decimal 0.1: 0.10000000000000000")
print(f"\n=== The Multiplication Results ===")
result32 = x32 * 3
result64 = x64 * 3
print(f"float32: {x32:.17f} × 3 = {result32:.17f}")
print(f"float64: {x64:.17f} × 3 = {result64:.17f}")
print(f"Expected: 0.3 exactly")
print(f"\n=== Error Analysis ===")
expected = 0.3
error32 = abs(result32 - expected)
error64 = abs(result64 - expected)
print(f"float32 error: {error32:.2e}")
print(f"float64 error: {error64:.2e}")
print(f"float64 is {error32/error64:.1f}x more accurate!")
=== The Results ===
float32: 0.10000000149011612 × 3 = 0.30000000447034836
float64: 0.1 × 3 = 0.30000000000000004
=== Why Are They Different? ===
The issue is that 0.1 cannot be represented exactly in binary!
Just like 1/3 = 0.333... in decimal (goes on forever),
0.1 in binary is 0.000110011001100... (repeating pattern)
=== How Each Type Stores 0.1 ===
float32 stores 0.1 as: 0.10000000149011612
float64 stores 0.1 as: 0.10000000000000001
True decimal 0.1: 0.10000000000000000
=== The Multiplication Results ===
float32: 0.10000000149011612 × 3 = 0.30000000447034836
float64: 0.10000000000000001 × 3 = 0.30000000000000004
Expected: 0.3 exactly
=== Error Analysis ===
float32 error: 4.47e-09
float64 error: 5.55e-17
float64 is 80530637.0x more accurate!
💡 Key Takeaway
Computers can’t store all decimal numbers exactly.
float64is more precise thanfloat32.For most engineering work, these tiny errors don’t matter.
But in long calculations, small errors can add up!
1.3 Python Functions#
In addition to basic mathematical operators, Python contains a number of built-in functions. As in mathematics, a function has a name (e.g., abs() or round()) and the arguments are placed inside parentheses after the name. An argument is any value or piece of information fed into a function.
For example, abs(-5) requires a single argument (the number -5) and returns its absolute value.
def round_custom(value):
integer_part = int(value)
frac_part = value - integer_part
if frac_part >= 0.5:
answer = integer_part + 1
else:
answer = integer_part
return answer
input_value = 3.9
print(round(input_value))
print(round_custom(input_value))
4
4
1.3.1 Common Python Math Functions#
Function |
Description |
Example |
Result |
|---|---|---|---|
|
Absolute value |
|
|
|
Round to nearest integer |
|
|
|
Round to n decimal places |
|
|
|
Maximum value |
|
|
|
Minimum value |
|
|
|
x to the power of y |
|
|
|
Length of sequence |
|
|
Important Note: The round() function uses “Banker’s rounding” (also called “round half to even”). If a number is exactly halfway between two integers (e.g., 4.5), it rounds toward the even integer. So round(4.5) = 4 and round(5.5) = 6.
Let’s try some examples:
round(4.5)
4
# Python Built-in Math Functions Examples
print("=== Absolute Value Function ===")
temperature_error = -2.5 # °C
print(f"Temperature error: {temperature_error}°C")
print(f"Absolute error: {abs(temperature_error)}°C")
print("\n=== Rounding Functions ===")
measurement = 15.6789 # some measured value
print(f"Original measurement: {measurement}")
print(f"Rounded to integer: {round(measurement)}")
print(f"Rounded to 2 decimal places: {round(measurement, 2)}")
print(f"Rounded to 1 decimal place: {round(measurement, 1)}")
print("\n=== Banker's Rounding Examples ===")
print(f"round(4.5) = {round(4.5)} (rounds to even)")
print(f"round(5.5) = {round(5.5)} (rounds to even)")
print(f"round(3.5) = {round(3.5)} (rounds to even)")
print(f"round(6.5) = {round(6.5)} (rounds to even)")
print("\n=== Min/Max Functions ===")
reactor_temps = [78.5, 82.1, 79.3, 85.7, 77.9] # °C
print(f"Reactor temperatures: {reactor_temps}")
print(f"Minimum temperature: {min(reactor_temps)}°C")
print(f"Maximum temperature: {max(reactor_temps)}°C")
print(f"Temperature range: {max(reactor_temps) - min(reactor_temps):.1f}°C")
print("\n=== Power and Length Functions ===")
print(f"pow(2, 3) = {pow(2, 3)} (same as 2**3 = {2**3})")
print(f"Length of temperature list: {len(reactor_temps)} measurements")
=== Absolute Value Function ===
Temperature error: -2.5°C
Absolute error: 2.5°C
=== Rounding Functions ===
Original measurement: 15.6789
Rounded to integer: 16
Rounded to 2 decimal places: 15.68
Rounded to 1 decimal place: 15.7
=== Banker's Rounding Examples ===
round(4.5) = 4 (rounds to even)
round(5.5) = 6 (rounds to even)
round(3.5) = 4 (rounds to even)
round(6.5) = 6 (rounds to even)
=== Min/Max Functions ===
Reactor temperatures: [78.5, 82.1, 79.3, 85.7, 77.9]
Minimum temperature: 77.9°C
Maximum temperature: 85.7°C
Temperature range: 7.8°C
=== Power and Length Functions ===
pow(2, 3) = 8 (same as 2**3 = 8)
Length of temperature list: 5 measurements
Other than mathematical operation Python has basic functions,l ike print(), open(), type(), etc
1.4 Variables#
Variables are like labeled containers that store values in your computer’s memory. Think of them as storage boxes with names written on them - you can put different things inside and refer to them by their labels.
A variable has three key components:
Name - What you call it (e.g.,
temperature,pressure)Value - What’s stored inside (e.g.,
85.5,"distillation")Type - What kind of data it is (e.g., number, text)
a = 1
print(a)
a = 2
print(a)
1
2
variable = 2
print('addition')
print(variable + 100)
print('subtraction')
print(variable -100)
print('multiplication')
print(variable *100)
addition
102
subtraction
-98
multiplication
200
1.4.1 Creating & Assigning Variables#
In Python, you create a variable by simply assigning a value to a name using the = sign:
temperature = 85.5 # Creates a variable named 'temperature'
process_name = "reactor" # Creates a variable named 'process_name'
sample_count = 10 # Creates a variable named 'sample_count'
Variable Naming Rules:
Required Rules (Python will give an error if broken):
Must start with a letter (a-z, A-Z) or underscore (_)
Can contain letters, numbers, and underscores
Cannot contain spaces or special characters (@, #, $, etc.)
Cannot be Python keywords (like
print,if,for)
Best Practices (for readable code):
Use descriptive names:
reactor_temperaturenottempUse lowercase with underscores:
flow_ratenotFlowRateAvoid confusing names:
pressure_1andpressure_2instead ofp1andp2
Abc = 123
abc = 123
_abc = 123
1abc
Cell In[11], line 1
1abc
^
SyntaxError: invalid decimal literal
#print('hello world')
# print = 123
# print
123
print('hello world')
hello world
a = 1
print(a)
b = 'hello world'
print(b)
1
hello world
variable = 1000
print(variable + 2)
print(variable - 2)
print(variable * 2)
print(variable / 2)
1002
998
2000
500.0
if = 123
Cell In[2], line 1
if = 123
^
SyntaxError: invalid syntax
Python Reserved Words (Keywords):
These words have special meanings in Python and cannot be used as variable names. Python will give you an error if you try to use them:
Reserved Word |
Purpose |
Example Usage |
|---|---|---|
|
Logical AND operator |
|
|
Logical OR operator |
|
|
Logical NOT operator |
|
|
Conditional statement |
|
|
Else-if in conditional |
|
|
Else in conditional |
|
|
For loop |
|
|
While loop |
|
|
Membership test |
|
|
Identity test |
|
|
Define function |
|
|
Define class |
|
|
Return from function |
|
|
Import module |
|
|
Import specific items |
|
|
Create alias |
|
|
Try block (error handling) |
|
|
Except block |
|
|
Finally block |
|
|
Context manager |
|
|
Anonymous function |
|
|
Boolean true value |
|
|
Boolean false value |
|
|
Null value |
|
Common Mistakes to Avoid:
# ❌ These will cause errors:
if = 85.5 # 'if' is reserved
for = "reactor" # 'for' is reserved
class = 10 # 'class' is reserved
# ✅ Use these instead:
condition = 85.5
process = "reactor"
category = 10
Pro Tip: If you accidentally use a reserved word, Python will show an error like:
SyntaxError: invalid syntax
Choose descriptive variable names that don’t conflict with these keywords!
Why Variables Matter in Engineering:
Store measurements:
pressure = 2.5,temperature = 85.0Track calculations:
efficiency = output / input * 100Label data:
process_name = "distillation"Reuse values: Calculate once, use many times
Make code readable:
reactor_volumeis clearer than just10
Let’s see variables in action:
1.4.2 Compound Assignment#
Compound assignment operators are shortcuts that combine an operation with variable assignment. Instead of writing temperature = temperature + 5, you can write temperature += 5. These operators make your code more concise and are commonly used in engineering calculations.
Compound Assignment Operators
Operator |
Equivalent |
Description |
Example |
Result |
|---|---|---|---|---|
|
|
Add and assign |
|
Increase temp by 10 |
|
|
Subtract and assign |
|
Decrease pressure by 0.5 |
|
|
Multiply and assign |
|
Double the volume |
|
|
Divide and assign |
|
Halve concentration |
|
|
Floor divide and assign |
|
Integer division |
|
|
Modulo and assign |
|
Get remainder |
|
|
Power and assign |
|
Square root of area |
Why Use Compound Assignment?
Shorter code:
count += 1vscount = count + 1Less typing: Fewer chances for typos
Clearer intent: Shows you’re modifying the existing variable
Common pattern: Very frequently used in loops and iterative calculations
temperature = 100 # Celcius
print(temperature)
temperature = temperature + 10
print(temperature)
temperature = 100 # Celcius
print(temperature)
temperature += 10
print(temperature)
100
110
100
110
a = 0
for i in [1,2,3,4,5]:
a += i
print(a)
1
3
6
10
15
# Compound Assignment Operators Examples
print("=== Basic Compound Assignment Examples ===")
# Starting values
temperature = 75.0 # °C
pressure = 2.0 # bar
volume = 100 # L
batch_count = 0
print(f"Initial values:")
print(f"Temperature: {temperature}°C")
print(f"Pressure: {pressure} bar")
print(f"Volume: {volume} L")
print(f"Batch count: {batch_count}")
print("\n=== Addition Assignment (+=) ===")
temperature += 15.5 # Same as: temperature = temperature + 15.5
batch_count += 1 # Increment batch counter
print(f"After heating: temperature = {temperature}°C")
print(f"After processing: batch_count = {batch_count}")
print("\n=== Subtraction Assignment (-=) ===")
pressure -= 0.3 # Pressure drop
volume -= 25 # Volume consumed
print(f"After pressure drop: pressure = {pressure} bar")
print(f"After consumption: volume = {volume} L")
print("\n=== Multiplication Assignment (*=) ===")
# Scale up production
production_rate = 50 # kg/hr
production_rate *= 1.5 # Increase by 50%
print(f"Scaled production rate: {production_rate} kg/hr")
print("\n=== Division Assignment (/=) ===")
concentration = 25.0 # mol/L
concentration /= 2 # Dilute by half
print(f"After dilution: concentration = {concentration} mol/L")
print("\n=== Power Assignment (**=) ===")
# Calculate area from diameter
diameter = 4.0 # m
radius = diameter / 2
area = radius # Start with radius
area **= 2 # Square it (r²)
area *= 3.14159 # Multiply by π
print(f"Circle area (d={diameter}m): {area:.2f} m²")
=== Basic Compound Assignment Examples ===
Initial values:
Temperature: 75.0°C
Pressure: 2.0 bar
Volume: 100 L
Batch count: 0
=== Addition Assignment (+=) ===
After heating: temperature = 90.5°C
After processing: batch_count = 1
=== Subtraction Assignment (-=) ===
After pressure drop: pressure = 1.7 bar
After consumption: volume = 75 L
=== Multiplication Assignment (*=) ===
Scaled production rate: 75.0 kg/hr
=== Division Assignment (/=) ===
After dilution: concentration = 12.5 mol/L
=== Power Assignment (**=) ===
Circle area (d=4.0m): 12.57 m²