Every year the wind over the Arabian Sea reverses. For half the year dry air blows from India out to sea, and for the other half wet air blows from the sea onto India and drops most of the country's rain. A monsoon is a sea breeze the size of a continent that turns around once a year. Three labs follow it: why the land wins the heating contest, how the wind bends as it crosses the equator, and why the rain lands on one side of the mountains.
The same sunshine falls on a patch of land and a patch of ocean at the same latitude. Land stores heat only in a thin layer, so it warms and cools within weeks. The sea mixes heat through a layer of water about deep and barely moves. When the land is warmer, air over it expands, a low forms, and the surface wind blows in from the sea. In winter the contrast flips and so does the wind. It's the W12 · Sea breeze on a yearly cycle.
How to read this: the top picture is a slice from the sea (left) to India (right). The big arrow near the ground is the surface wind: blue means moist sea air blowing onto land (the rainy monsoon), amber means dry air blowing out to sea. Gold H and L mark higher and lower air pressure, and the surface wind blows from H toward L. The graph below tracks land and sea temperature over the year; the green-shaded stretches are when the land is warmer and the wind blows in from the sea.
Try this: press Play year and watch the arrow flip color and direction twice a year.
Q is the daily-mean sunshine at the chosen latitude, α = 0.33, A + B·T is outgoing infrared (A = 203.3 W/m², B = 2.09 W/m²/K, T in °C), and k = 3 W/m²/K stands in for air carrying heat between the two. Cland = 1.0×10⁷ J/m²/K, about the heat capacity of the air column itself. Csea = ρc·(mixed-layer depth), 2×10⁸ J/m²/K for .
The pressure line assumes the warm column's extra thickness has already pushed air out aloft, so pressure is level at height h and the surface pressure differs by the lost weight.
The moist air that flows in rises over the land and its vapor condenses. Condensation releases latent heat, about 2.5 million joules per kilogram of rain, right in the middle of the column. That heat deepens the low, which pulls in more moist air. The real monsoon is stronger and steadier than this dry model because of that loop. The Tibetan Plateau adds to it: it sits up and heats the middle of the atmosphere directly, so the warm layer is much deeper than over lowland.
Rain clouds also shade the ground, so real Indian temperatures peak in May, before the rains, and fall once the monsoon arrives. This model has no clouds, so its land keeps warming into summer. For the global pattern these winds sit inside, see W04 · Global circulation.
from math import sin, cos, tan, acos, pi, radians, exp S0, A, B, k, albedo = 1361.0, 203.3, 2.09, 3.0, 0.33 C_land = 1.0e7 # J/m^2/K, about one air column C_sea = 1025 * 3990 * 50 # J/m^2/K, 50 m mixed layer def insolation(lat, day): # daily-mean W/m^2, circular orbit phi = radians(lat) dec = radians(23.44) * sin(2*pi*(day - 80)/365) x = max(-1.0, min(1.0, -tan(phi)*tan(dec))) h0 = acos(x) # sunset hour angle return S0/pi * (h0*sin(phi)*sin(dec) + cos(phi)*cos(dec)*sin(h0)) def seasons(lat=20, years=12): # daily steps; keep the last year TL = TS = 27.0 out = [] for y in range(years): for day in range(1, 366): if y == years - 1: out.append((day, TL, TS)) q = (1 - albedo) * insolation(lat, day) dTL = (q - (A + B*TL) - k*(TL - TS)) / C_land dTS = (q - (A + B*TS) + k*(TL - TS)) / C_sea TL += 86400 * dTL TS += 86400 * dTS return out def surface_dp(TL, TS, h=1500.0): # Pa, land minus sea Tm = 273.15 + (TL + TS)/2 Hs = 287*Tm/9.81 # scale height, m mass = 101325/(287*Tm) * Hs * (1 - exp(-h/Hs)) # kg/m^2 return -9.81 * mass * (TL - TS) / Tm
Air flows toward the low-pressure trough where the trade winds meet, the Intertropical Convergence Zone (ITCZ). In July the trough sits over northern India, so air from the southern Indian Ocean has to cross the equator to reach it. Earth's rotation pushes moving air left in the Southern Hemisphere and right in the Northern. The southeast trade wind therefore turns into a southwest wind once it crosses, which is the wind that brings the rain.
How to read this: each dot is a bit of air, and its green tail shows where it has been. Follow one across the equator (the solid line): it turns from heading northwest to heading northeast. That bend makes the southwest monsoon.
Try this: switch to Without Coriolis and the bend disappears, so the air runs straight toward the trough.
F(φ) = −(1/ρ)·∂p/∂y is the push toward the trough from a pressure field p = pITCZ + Δp·(1 − e−s²), s = (y − yITCZ)/. The pressure depends only on latitude and the map's coastlines are drawn for orientation, so the model ignores mountains and coasts. Parcels are stepped every 10 minutes with a midpoint (RK2) scheme.
f changes sign at the equator. South of it f is negative, so northward motion (v > 0) makes du/dt negative and the air gains an east-to-west component: a southeasterly. North of the equator f is positive and the same northward motion builds a west-to-east component: a southwesterly. At the equator itself f = 0 and the air runs straight down the pressure gradient.
In the real ocean the East African highlands squeeze the crossing into a fast low-level jet off Somalia, which this flat model can't make. In January the trough moves south of the equator and the whole picture runs backward: dry northeasterlies over India, turning northwesterly over northern Australia.
from math import sin, exp, cos, pi, radians, degrees OMEGA, R_E, RHO = 7.292e-5, 6.371e6, 1.15 r = 1/(12*3600) # linear drag, 1/s W = 4.0e6 # width of the trough, m def itcz_lat(day): # prescribed, about -10 to 25 deg return 7.5 - 17.5*cos(2*pi*(day - 20)/365) def push(y, y_itcz, dp=1500.0): # F = -(1/rho) dp/dy, m/s^2 s = (y - y_itcz) / W return -(dp/RHO) * exp(-s*s) * 2*s / W def steady_wind(lat, day, dp=1500.0, coriolis=True): f = 2*OMEGA*sin(radians(lat)) if coriolis else 0.0 y, yi = radians(lat)*R_E, radians(itcz_lat(day))*R_E F = push(y, yi, dp) u = f*F / (r*r + f*f) # east-west, m/s v = r*F / (r*r + f*f) # north-south, m/s return u, v def parcel(lat, u, v, day, hours=96, dt=600.0): y, yi = radians(lat)*R_E, radians(itcz_lat(day))*R_E path = [] def rate(y, u, v): f = 2*OMEGA*sin(y/R_E) return f*v - r*u, -f*u - r*v + push(y, yi), v for _ in range(int(hours*3600/dt)): # midpoint RK2 du, dv, dy = rate(y, u, v) du, dv, dy = rate(y + dy*dt/2, u + du*dt/2, v + dv*dt/2) u, v, y = u + du*dt, v + dv*dt, y + dy*dt path.append((degrees(y/R_E), u, v)) return path
When monsoon air meets a mountain range it has to climb. Rising air expands and cools, and once it reaches its condensation level the vapor turns to cloud and rain. The windward slope gets drenched. The air that comes down the far side has lost much of its water and warms as it sinks, so the land behind the range sits in a rain shadow. The Western Ghats catch the monsoon off the Arabian Sea this way, and the Himalaya wall off the Tibetan Plateau.
How to read this: wind blows from the left up and over the mountain. The faint lines are the paths the air takes. Once the air is pushed above the green dashed line it makes cloud and rain (blue streaks). Past the top, the air sinks and dries out. Heights are stretched so the mountain is visible. The strip below shows how hard it rains at each spot along the same slice.
Try this: click Himalaya, then drop Humidity to 50% and watch the gold hump shrink.
W is the water vapor in the column (kg/m², which equals millimeters of liquid). Lifting a saturated column by a small height δ condenses about ρv·δ of it. Half of that, ε, reaches the ground and the rest drifts on as cloud. Rain starts only where the ground has lifted the air past its condensation level, 125 m per °C of dew-point depression. Surface air is held at , Rv = 461.5 J/(kg·K).
Integrating gives W after the crest = W0·exp(−ε·(H − zLCL)/Hw). The model drops rain where it forms. Real drops drift downwind and real air can flow around a ridge, so treat the numbers as an order of magnitude.
from math import exp, log RV, H_W, EFF = 461.5, 2500.0, 0.5 def e_sat(Tc): # Pa, Bolton (1980) return 611.2 * exp(17.67*Tc/(Tc + 243.5)) def lcl(Tc, RH): # m, 125 m per deg C g = log(RH) + 17.67*Tc/(Tc + 243.5) Td = 243.5*g/(17.67 - g) return 125*(Tc - Td) def ridge(x, H, a_wind, a_lee): # m, two half-Gaussians a = a_wind if x < 0 else a_lee return H*exp(-(x/a)**2) def rain_profile(U, RH, H, a_wind, a_lee, Tc=27.0, n=600): rho_v = RH*e_sat(Tc) / (RV*(Tc + 273.15)) # kg/m^3 W = rho_v * H_W # kg/m^2 = mm z_lcl = lcl(Tc, RH) x0, x1 = -3.2*a_wind, 2.5*a_lee dx = (x1 - x0) / n out, h_prev = [], ridge(x0, H, a_wind, a_lee) for i in range(1, n + 1): x = x0 + i*dx h = ridge(x, H, a_wind, a_lee) lift = h - max(h_prev, z_lcl) # saturated lift P = 0.0 if h > h_prev and lift > 0: W_new = W * exp(-EFF*lift/H_W) P = (W - W_new) * U / dx # kg/m^2/s W = W_new out.append((x, h, P*86400, W)) # rain in mm/day h_prev = h return out