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 Typez₀ (m)
Ice, mud flats10⁻⁵
Open sea10⁻⁴
Grass10⁻² - 10⁻¹
Suburban0.5 - 2
Urban/forest1 - 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")