3.3 Boundary Layer
The atmospheric boundary layer (ABL) is the lowest 1-2 km of the atmosphere where surface friction significantly affects airflow. Turbulent mixing is the dominant process.
Ekman Spiral
Surface friction reduces wind speed and causes the wind direction to turn toward low pressure. The wind direction changes with height, forming a spiral pattern.
$$u = u_g(1 - e^{-z/D}\cos(z/D))$$
$$v = u_g e^{-z/D}\sin(z/D)$$
D = Ekman depth ≈ 1 km
Surface Wind
~30° toward low pressure from isobars
Ekman Pumping
Convergence in lows → rising motion
Turbulent Mixing
Eddy Viscosity
$$\tau = \rho K_m \frac{\partial u}{\partial z}$$
Km ~ 1-100 m²/s in the boundary layer
Mixing Length
$$l = \kappa z$$
von Kármán constant κ ≈ 0.4
Logarithmic Wind Profile
$$u(z) = \frac{u_*}{\kappa}\ln\left(\frac{z}{z_0}\right)$$
u* = friction velocity, z₀ = roughness length
| Surface Type | z₀ (m) |
|---|---|
| Ice, mud flats | 10⁻⁵ |
| Open sea | 10⁻⁴ |
| Grass | 10⁻² - 10⁻¹ |
| Suburban | 0.5 - 2 |
| Urban/forest | 1 - 5 |
Python: Boundary Layer Profiles
#!/usr/bin/env python3
"""
boundary_layer.py - Atmospheric boundary layer wind profiles
Run: python3 boundary_layer.py
Requires: pip install numpy matplotlib
"""
import numpy as np
import matplotlib.pyplot as plt
# Constants
kappa = 0.4 # von Kármán constant
def log_wind_profile(z, u_star, z0):
"""Logarithmic wind profile"""
return (u_star / kappa) * np.log(z / z0)
def ekman_spiral(z, u_g, D):
"""Ekman spiral wind components"""
u = u_g * (1 - np.exp(-z/D) * np.cos(z/D))
v = u_g * np.exp(-z/D) * np.sin(z/D)
return u, v
# Height array
z = np.linspace(1, 1000, 200) # m
# Plot 1: Logarithmic profiles for different surfaces
fig, axes = plt.subplots(1, 3, figsize=(15, 6))
ax1 = axes[0]
u_star = 0.3 # m/s
surfaces = {'Ocean': 1e-4, 'Grass': 0.03, 'Suburban': 1.0, 'Forest': 2.0}
for name, z0 in surfaces.items():
valid = z > z0
u = log_wind_profile(z[valid], u_star, z0)
ax1.semilogx(u, z[valid], label=f'{name} (z₀={z0})', linewidth=2)
ax1.set_xlabel('Wind Speed (m/s)', fontsize=12)
ax1.set_ylabel('Height (m)', fontsize=12)
ax1.set_title(f'Logarithmic Wind Profile (u* = {u_star} m/s)', fontsize=14)
ax1.legend()
ax1.grid(True, alpha=0.3)
ax1.set_ylim(0, 200)
ax1.set_xlim(0, 12)
# Plot 2: Ekman spiral
ax2 = axes[1]
u_g = 10 # geostrophic wind (m/s)
D = 1000 # Ekman depth (m)
z_ekman = np.linspace(0, 2000, 100)
u_ek, v_ek = ekman_spiral(z_ekman, u_g, D)
ax2.plot(u_ek, z_ekman, 'b-', linewidth=2, label='u (east-west)')
ax2.plot(v_ek, z_ekman, 'r-', linewidth=2, label='v (north-south)')
ax2.axhline(y=D, color='gray', linestyle='--', label='Ekman depth')
ax2.axvline(x=u_g, color='green', linestyle='--', label='Geostrophic wind')
ax2.set_xlabel('Wind Component (m/s)', fontsize=12)
ax2.set_ylabel('Height (m)', fontsize=12)
ax2.set_title('Ekman Layer Wind Profile', fontsize=14)
ax2.legend()
ax2.grid(True, alpha=0.3)
# Plot 3: Ekman spiral hodograph
ax3 = axes[2]
ax3.plot(u_ek, v_ek, 'purple', linewidth=2)
ax3.plot(u_g, 0, 'go', markersize=10, label='Geostrophic')
ax3.plot(u_ek[0], v_ek[0], 'ro', markersize=10, label='Surface')
# Add height markers
for i, h in enumerate([0, 200, 500, 1000]):
idx = np.argmin(np.abs(z_ekman - h))
ax3.annotate(f'{h}m', (u_ek[idx], v_ek[idx]), fontsize=8)
ax3.set_xlabel('u (m/s)', fontsize=12)
ax3.set_ylabel('v (m/s)', fontsize=12)
ax3.set_title('Ekman Spiral (Hodograph)', fontsize=14)
ax3.legend()
ax3.grid(True, alpha=0.3)
ax3.set_aspect('equal')
plt.tight_layout()
plt.savefig('boundary_layer.png', dpi=150, bbox_inches='tight')
plt.show()
# Print some values
print("Surface wind direction deflection from geostrophic:")
u0, v0 = ekman_spiral(10, u_g, D) # at 10m
angle = np.degrees(np.arctan2(v0, u0))
print(f" At 10m: {angle:.1f}° toward low pressure")
print("\nLogarithmic profile wind speeds at 10m (u* = 0.3 m/s):")
for name, z0 in surfaces.items():
u_10 = log_wind_profile(10, 0.3, z0)
print(f" {name}: {u_10:.1f} m/s")