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}")