2.1 Thermodynamic Principles
Atmospheric thermodynamics applies the fundamental laws of thermodynamics to understand heat transfer, work, and energy transformations in the atmosphere.
First Law of Thermodynamics
$$dU = \delta Q - \delta W = \delta Q - p \, dV$$
Change in internal energy = heat added - work done
For an ideal gas:
Internal Energy
$$U = n c_v T$$
Enthalpy
$$H = U + pV = n c_p T$$
Heat Capacities
Specific Heat at Constant Pressure
cp = 1005 J/(kg·K) for dry air
Specific Heat at Constant Volume
cv = 718 J/(kg·K) for dry air
Ratio of Heat Capacities
$\gamma = c_p/c_v = 1.4$ for diatomic gases
Mayer's relation: $c_p - c_v = R_d$ (287 J/(kg·K) for dry air)
Python: Thermodynamic Processes
#!/usr/bin/env python3
"""
atm_thermodynamics.py - Atmospheric thermodynamic processes
Run: python3 atm_thermodynamics.py
Requires: pip install numpy matplotlib
"""
import numpy as np
import matplotlib.pyplot as plt
# Constants for dry air
R_d = 287.0 # J/(kg·K) - gas constant
c_p = 1005.0 # J/(kg·K) - specific heat at constant pressure
c_v = 718.0 # J/(kg·K) - specific heat at constant volume
gamma = c_p / c_v # ≈ 1.4
# Reference state
p0 = 100000 # Pa (1000 hPa)
T0 = 300 # K
# Volume for 1 kg of air
V0 = R_d * T0 / p0
# Generate different thermodynamic processes
V = np.linspace(0.5 * V0, 2 * V0, 100)
# 1. Isothermal (constant T): pV = const
p_isothermal = p0 * V0 / V
# 2. Isobaric (constant p): V/T = const
T_isobaric = T0 * V / V0
p_isobaric = np.ones_like(V) * p0
# 3. Adiabatic (no heat exchange): pV^γ = const
p_adiabatic = p0 * (V0 / V)**gamma
# 4. Isochoric (constant V): p/T = const
# (shown as vertical line at V0)
# Plot p-V diagram
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
ax1 = axes[0]
ax1.plot(V * 1000, p_isothermal / 1000, 'r-', linewidth=2, label='Isothermal (T = const)')
ax1.plot(V * 1000, p_adiabatic / 1000, 'b-', linewidth=2, label='Adiabatic (Q = 0)')
ax1.axhline(y=p0/1000, color='g', linewidth=2, label='Isobaric (p = const)')
ax1.axvline(x=V0*1000, color='purple', linewidth=2, label='Isochoric (V = const)')
ax1.set_xlabel('Volume (L/kg)', fontsize=12)
ax1.set_ylabel('Pressure (kPa)', fontsize=12)
ax1.set_title('Thermodynamic Processes (p-V Diagram)', fontsize=14)
ax1.legend()
ax1.grid(True, alpha=0.3)
ax1.set_xlim(0.4, 1.8)
ax1.set_ylim(30, 200)
# Potential temperature vs pressure
ax2 = axes[1]
p_levels = np.linspace(100000, 20000, 100) # Pa
kappa = R_d / c_p # ≈ 0.286
# Calculate potential temperature for parcels at different initial temperatures
for T_initial in [280, 290, 300, 310]:
# θ is constant for adiabatic process
theta = T_initial * (p0 / p0)**kappa # = T_initial
# Actual temperature at each pressure level
T = theta * (p_levels / p0)**kappa
ax2.plot(T, p_levels / 100, label=f'θ = {theta:.0f} K')
ax2.set_xlabel('Temperature (K)', fontsize=12)
ax2.set_ylabel('Pressure (hPa)', fontsize=12)
ax2.set_title('Adiabatic Temperature Change', fontsize=14)
ax2.invert_yaxis()
ax2.legend()
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('atm_thermodynamics.png', dpi=150, bbox_inches='tight')
plt.show()
print("Thermodynamic Constants:")
print(f" R_d = {R_d} J/(kg·K)")
print(f" c_p = {c_p} J/(kg·K)")
print(f" c_v = {c_v} J/(kg·K)")
print(f" γ = {gamma:.3f}")
print(f" κ = R_d/c_p = {R_d/c_p:.4f}")