2.4 Adiabatic Processes
Adiabatic processes involve no heat exchange with the environment. As air parcels rise and expand (or sink and compress), their temperature changes predictably.
Dry Adiabatic Lapse Rate
$$\Gamma_d = -\frac{dT}{dz} = \frac{g}{c_p} = 9.8 \text{ K/km}$$
Rate of temperature decrease for unsaturated rising air
Derived from the first law of thermodynamics with δQ = 0 and hydrostatic balance. Approximately 10°C per 1000m of ascent.
Moist Adiabatic Lapse Rate
$$\Gamma_m = \Gamma_d \frac{1 + \frac{L_v r_s}{R_d T}}{1 + \frac{L_v^2 r_s}{c_p R_v T^2}}$$
Varies from ~4-9 K/km depending on temperature and moisture
Warm, Moist Air
Γm ≈ 4-5 K/km
More latent heat release
Cold, Dry Air
Γm ≈ 8-9 K/km
Approaches dry adiabatic
Potential Temperature
$$\theta = T \left(\frac{p_0}{p}\right)^{R_d/c_p} = T \left(\frac{p_0}{p}\right)^{0.286}$$
Temperature a parcel would have if brought adiabatically to 1000 hPa
Key Properties
- • Conserved during dry adiabatic processes
- • Constant on isentropic surfaces
- • ∂θ/∂z > 0 indicates stable stratification
- • Used to compare air masses at different altitudes
Python: Adiabatic Processes
#!/usr/bin/env python3
"""
adiabatic_processes.py - Adiabatic lapse rates and potential temperature
Run: python3 adiabatic_processes.py
Requires: pip install numpy matplotlib
"""
import numpy as np
import matplotlib.pyplot as plt
# Constants
g = 9.81 # m/s²
R_d = 287.0 # J/(kg·K)
R_v = 461.5 # J/(kg·K)
c_p = 1005.0 # J/(kg·K)
c_v = 718.0 # J/(kg·K)
L_v = 2.5e6 # J/kg
p0 = 100000 # Pa (reference pressure for θ)
epsilon = R_d / R_v
# Dry adiabatic lapse rate
Gamma_d = g / c_p * 1000 # K/km
def saturation_mixing_ratio(T, p):
"""Saturation mixing ratio (kg/kg)"""
e_s = 6.11 * np.exp(17.27 * (T - 273.15) / (T - 35.85)) * 100 # Pa
return epsilon * e_s / (p - e_s)
def moist_adiabatic_lapse_rate(T, p):
"""Moist adiabatic lapse rate (K/km)"""
r_s = saturation_mixing_ratio(T, p)
numerator = 1 + L_v * r_s / (R_d * T)
denominator = 1 + L_v**2 * r_s / (c_p * R_v * T**2)
return Gamma_d * numerator / denominator
def potential_temperature(T, p):
"""Potential temperature (K)"""
return T * (p0 / p) ** (R_d / c_p)
# Altitude and pressure profile
z = np.linspace(0, 15000, 100) # m
p = p0 * np.exp(-z / 8500) # hydrostatic approximation
# Parcel ascent: dry then moist
T_sfc = 300 # K
T_dry = np.zeros_like(z)
T_moist = np.zeros_like(z)
theta = np.zeros_like(z)
T_dry[0] = T_sfc
T_moist[0] = T_sfc
theta[0] = potential_temperature(T_sfc, p[0])
# Find LCL (assume initial RH = 80%)
RH_initial = 0.80
r_initial = RH_initial * saturation_mixing_ratio(T_sfc, p[0])
# Rough LCL calculation
lcl_idx = 20 # approximately
for i in range(1, len(z)):
dz = z[i] - z[i-1]
# Dry adiabatic
T_dry[i] = T_dry[i-1] - Gamma_d * dz / 1000
theta[i] = potential_temperature(T_dry[i], p[i])
# Moist adiabatic (after LCL)
if i < lcl_idx:
T_moist[i] = T_moist[i-1] - Gamma_d * dz / 1000
else:
Gamma_m = moist_adiabatic_lapse_rate(T_moist[i-1], p[i-1])
T_moist[i] = T_moist[i-1] - Gamma_m * dz / 1000
# Plot
fig, axes = plt.subplots(1, 3, figsize=(15, 8))
# Temperature profiles
ax1 = axes[0]
ax1.plot(T_dry - 273.15, z/1000, 'r-', linewidth=2, label='Dry Adiabatic')
ax1.plot(T_moist - 273.15, z/1000, 'b-', linewidth=2, label='Moist Adiabatic')
ax1.axhline(y=z[lcl_idx]/1000, color='green', linestyle='--', label=f'LCL ≈ {z[lcl_idx]/1000:.1f} km')
ax1.set_xlabel('Temperature (°C)', fontsize=12)
ax1.set_ylabel('Altitude (km)', fontsize=12)
ax1.set_title('Adiabatic Temperature Profiles', fontsize=14)
ax1.legend()
ax1.grid(True, alpha=0.3)
ax1.set_xlim(-80, 30)
# Potential temperature
ax2 = axes[1]
ax2.plot(theta, z/1000, 'purple', linewidth=2)
ax2.axhline(y=z[lcl_idx]/1000, color='green', linestyle='--')
ax2.set_xlabel('Potential Temperature θ (K)', fontsize=12)
ax2.set_ylabel('Altitude (km)', fontsize=12)
ax2.set_title('Potential Temperature Profile', fontsize=14)
ax2.grid(True, alpha=0.3)
# θ is constant for dry adiabatic
ax2.annotate('θ = const (dry adiabatic)', xy=(theta[10], z[10]/1000),
xytext=(320, 5), fontsize=10, color='purple',
arrowprops=dict(arrowstyle='->', color='purple'))
# Moist adiabatic lapse rate vs temperature
ax3 = axes[2]
T_range = np.linspace(250, 310, 100)
Gamma_m_values = [moist_adiabatic_lapse_rate(T, 85000) for T in T_range]
ax3.plot(T_range - 273.15, Gamma_m_values, 'b-', linewidth=2, label='Γ_m at 850 hPa')
ax3.axhline(y=Gamma_d, color='r', linestyle='--', linewidth=2, label=f'Γ_d = {Gamma_d:.1f} K/km')
ax3.set_xlabel('Temperature (°C)', fontsize=12)
ax3.set_ylabel('Lapse Rate (K/km)', fontsize=12)
ax3.set_title('Moist Adiabatic Lapse Rate', fontsize=14)
ax3.legend()
ax3.grid(True, alpha=0.3)
ax3.set_ylim(3, 11)
plt.tight_layout()
plt.savefig('adiabatic_processes.png', dpi=150, bbox_inches='tight')
plt.show()
print("Adiabatic Lapse Rates:")
print(f" Dry adiabatic: Γ_d = {Gamma_d:.2f} K/km")
print(f" Moist adiabatic at 0°C: {moist_adiabatic_lapse_rate(273.15, 85000):.2f} K/km")
print(f" Moist adiabatic at 20°C: {moist_adiabatic_lapse_rate(293.15, 85000):.2f} K/km")
print(f"\nPotential Temperature:")
print(f" θ(300 K, 1000 hPa) = {potential_temperature(300, 100000):.1f} K")
print(f" θ(280 K, 700 hPa) = {potential_temperature(280, 70000):.1f} K")