2.2 Moisture & Humidity

Water vapor is the most important variable constituent of the atmosphere. Its phase changes release and absorb latent heat, driving convection and weather systems.

Humidity Variables

Mixing Ratio (r)

$$r = \frac{m_v}{m_d} = \frac{\rho_v}{\rho_d}$$

Mass of water vapor per mass of dry air (g/kg)

Specific Humidity (q)

$$q = \frac{m_v}{m_v + m_d} = \frac{r}{1+r} \approx r$$

Mass of water vapor per total mass (g/kg)

Relative Humidity (RH)

$$RH = \frac{e}{e_s} \times 100\%$$

Ratio of actual to saturation vapor pressure

Clausius-Clapeyron Equation

$$\frac{de_s}{dT} = \frac{L_v e_s}{R_v T^2}$$

Rate of change of saturation vapor pressure with temperature

Integrated form (Tetens formula):

$$e_s(T) = 6.11 \exp\left(\frac{17.27 (T-273.15)}{T - 35.85}\right) \text{ hPa}$$

Saturation vapor pressure approximately doubles for every 10°C increase

Dew Point Temperature

Definition

Temperature to which air must be cooled at constant pressure to become saturated

Dew Point Depression

T - Td: larger values indicate drier air

Python: Humidity Calculations

#!/usr/bin/env python3
"""
moisture.py - Humidity and moisture calculations

Run: python3 moisture.py
Requires: pip install numpy matplotlib
"""
import numpy as np
import matplotlib.pyplot as plt

# Constants
R_d = 287.0   # J/(kg·K) - gas constant for dry air
R_v = 461.5   # J/(kg·K) - gas constant for water vapor
L_v = 2.5e6   # J/kg - latent heat of vaporization
epsilon = R_d / R_v  # ≈ 0.622

def saturation_vapor_pressure(T_celsius):
    """Tetens formula for saturation vapor pressure (hPa)"""
    return 6.11 * np.exp(17.27 * T_celsius / (T_celsius + 237.3))

def dew_point(e):
    """Dew point from vapor pressure (hPa) -> °C"""
    return 237.3 * np.log(e / 6.11) / (17.27 - np.log(e / 6.11))

def mixing_ratio(e, p):
    """Mixing ratio from vapor pressure and total pressure (g/kg)"""
    return 1000 * epsilon * e / (p - e)

# Temperature range
T = np.linspace(-40, 40, 100)  # °C
e_s = saturation_vapor_pressure(T)

# Plot Clausius-Clapeyron curve
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# Saturation vapor pressure vs temperature
ax1 = axes[0, 0]
ax1.semilogy(T, e_s, 'b-', linewidth=2)
ax1.set_xlabel('Temperature (°C)', fontsize=12)
ax1.set_ylabel('Saturation Vapor Pressure (hPa)', fontsize=12)
ax1.set_title('Clausius-Clapeyron: Saturation Vapor Pressure', fontsize=14)
ax1.grid(True, alpha=0.3, which='both')
ax1.axhline(y=6.11, color='gray', linestyle='--', alpha=0.5)
ax1.annotate('e_s(0°C) = 6.11 hPa', (0, 6.11), xytext=(10, 10), fontsize=10)

# Saturation mixing ratio
ax2 = axes[0, 1]
p_surface = 1013.25  # hPa
r_s = mixing_ratio(e_s, p_surface)
ax2.plot(T, r_s, 'g-', linewidth=2)
ax2.set_xlabel('Temperature (°C)', fontsize=12)
ax2.set_ylabel('Saturation Mixing Ratio (g/kg)', fontsize=12)
ax2.set_title(f'Saturation Mixing Ratio (p = {p_surface} hPa)', fontsize=14)
ax2.grid(True, alpha=0.3)

# Relative humidity example
ax3 = axes[1, 0]
T_air = 25  # °C
e_s_25 = saturation_vapor_pressure(T_air)
RH_values = np.linspace(10, 100, 10)
e_values = RH_values / 100 * e_s_25
T_d_values = dew_point(e_values)

ax3.bar(RH_values, T_d_values, width=8, color='cyan', alpha=0.7, edgecolor='blue')
ax3.axhline(y=T_air, color='red', linestyle='--', label=f'Air Temperature = {T_air}°C')
ax3.set_xlabel('Relative Humidity (%)', fontsize=12)
ax3.set_ylabel('Dew Point (°C)', fontsize=12)
ax3.set_title('Dew Point vs Relative Humidity (T = 25°C)', fontsize=14)
ax3.legend()
ax3.grid(True, alpha=0.3)

# Water vapor increase with warming
ax4 = axes[1, 1]
T_base = np.array([10, 15, 20, 25, 30, 35])
e_s_base = saturation_vapor_pressure(T_base)
e_s_warm = saturation_vapor_pressure(T_base + 1)  # 1°C warming
pct_increase = (e_s_warm - e_s_base) / e_s_base * 100

ax4.bar(T_base, pct_increase, width=3, color='orange', alpha=0.7, edgecolor='red')
ax4.axhline(y=7, color='red', linestyle='--', label='~7%/°C (Clausius-Clapeyron)')
ax4.set_xlabel('Temperature (°C)', fontsize=12)
ax4.set_ylabel('% Increase in e_s per 1°C', fontsize=12)
ax4.set_title('Water-Holding Capacity Increase', fontsize=14)
ax4.legend()
ax4.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('moisture.png', dpi=150, bbox_inches='tight')
plt.show()

# Example calculation
print("\nExample: T = 25°C, RH = 60%")
e_s_ex = saturation_vapor_pressure(25)
e_ex = 0.6 * e_s_ex
T_d_ex = dew_point(e_ex)
r_ex = mixing_ratio(e_ex, 1013.25)
print(f"  Saturation vapor pressure: {e_s_ex:.2f} hPa")
print(f"  Actual vapor pressure: {e_ex:.2f} hPa")
print(f"  Dew point: {T_d_ex:.1f}°C")
print(f"  Mixing ratio: {r_ex:.1f} g/kg")