Path: blob/main/latex-templates/templates/oceanography/ocean_currents.tex
51 views
unlisted
\documentclass[a4paper, 11pt]{article}1\usepackage[utf8]{inputenc}2\usepackage[T1]{fontenc}3\usepackage{amsmath, amssymb}4\usepackage{graphicx}5\usepackage{booktabs}6\usepackage{siunitx}7\usepackage[makestderr]{pythontex}89\title{Ocean Currents: Geostrophic Flow and Wind-Driven Circulation}10\author{Physical Oceanography Templates}11\date{\today}1213\begin{document}14\maketitle1516\section{Introduction}17Ocean currents are driven by wind stress, density differences, and Earth's rotation. This template explores geostrophic balance, Ekman spiral dynamics, Sverdrup transport, and western boundary current intensification.1819\section{Mathematical Framework}2021\subsection{Geostrophic Balance}22Large-scale ocean flow balances Coriolis force and pressure gradient:23\begin{equation}24f \mathbf{k} \times \mathbf{u}_g = -\frac{1}{\rho_0} \nabla p25\end{equation}26where $f = 2\Omega \sin\phi$ is the Coriolis parameter.2728Component form:29\begin{align}30u_g &= -\frac{1}{\rho_0 f} \frac{\partial p}{\partial y} \\31v_g &= \frac{1}{\rho_0 f} \frac{\partial p}{\partial x}32\end{align}3334\subsection{Ekman Transport}35Wind stress drives Ekman transport perpendicular to wind direction:36\begin{equation}37\mathbf{M}_E = \frac{\boldsymbol{\tau} \times \mathbf{k}}{\rho_0 f}38\end{equation}3940The Ekman spiral describes velocity decay with depth:41\begin{align}42u(z) &= V_0 e^{z/D_E} \cos\left(\frac{\pi}{4} + \frac{z}{D_E}\right) \\43v(z) &= V_0 e^{z/D_E} \sin\left(\frac{\pi}{4} + \frac{z}{D_E}\right)44\end{align}45where $D_E = \sqrt{2A_v/f}$ is the Ekman depth.4647\subsection{Sverdrup Balance}48Wind stress curl drives meridional transport:49\begin{equation}50\beta V = \frac{1}{\rho_0} \nabla \times \boldsymbol{\tau}51\end{equation}52where $\beta = \partial f/\partial y$ is the planetary vorticity gradient.5354\subsection{Western Boundary Current}55Munk's model includes lateral friction:56\begin{equation}57\beta v = A_H \nabla^4 \psi + \frac{1}{\rho_0 H} \left(\frac{\partial \tau_y}{\partial x} - \frac{\partial \tau_x}{\partial y}\right)58\end{equation}5960\section{Environment Setup}6162\begin{pycode}63import numpy as np64import matplotlib.pyplot as plt65from scipy.integrate import odeint6667plt.rc('text', usetex=True)68plt.rc('font', family='serif')69np.random.seed(42)7071def save_plot(filename, caption=""):72plt.savefig(filename, bbox_inches='tight', dpi=150)73print(r'\begin{figure}[htbp]')74print(r'\centering')75print(r'\includegraphics[width=0.9\textwidth]{' + filename + '}')76if caption:77print(r'\caption{' + caption + '}')78print(r'\end{figure}')79plt.close()8081# Physical constants82Omega = 7.292e-5 # Earth's rotation rate (rad/s)83rho_0 = 1025 # Reference density (kg/m^3)84g = 9.81 # Gravity (m/s^2)85\end{pycode}8687\section{Geostrophic Flow Computation}8889\begin{pycode}90# Create pressure field for geostrophic calculation91nx, ny = 50, 5092x = np.linspace(0, 1000e3, nx) # 1000 km domain93y = np.linspace(0, 1000e3, ny)94X, Y = np.meshgrid(x, y)9596# Sea surface height anomaly (mesoscale eddy)97eta = 0.3 * np.exp(-((X - 500e3)**2 + (Y - 500e3)**2) / (200e3)**2)9899# Coriolis parameter (mid-latitude)100lat = 30 # degrees101f = 2 * Omega * np.sin(np.radians(lat))102beta = 2 * Omega * np.cos(np.radians(lat)) / 6.371e6103104# Geostrophic velocities from SSH gradient105dx = x[1] - x[0]106dy = y[1] - y[0]107108deta_dx = np.gradient(eta, dx, axis=1)109deta_dy = np.gradient(eta, dy, axis=0)110111u_g = -g / f * deta_dy112v_g = g / f * deta_dx113114speed = np.sqrt(u_g**2 + v_g**2)115116# Visualization117fig, axes = plt.subplots(2, 2, figsize=(12, 10))118119# Plot 1: Sea surface height120im1 = axes[0, 0].contourf(X/1e3, Y/1e3, eta*100, levels=20, cmap='RdBu_r')121axes[0, 0].set_xlabel('x (km)')122axes[0, 0].set_ylabel('y (km)')123axes[0, 0].set_title('Sea Surface Height Anomaly (cm)')124plt.colorbar(im1, ax=axes[0, 0])125126# Plot 2: Geostrophic velocity vectors127skip = 3128axes[0, 1].quiver(X[::skip, ::skip]/1e3, Y[::skip, ::skip]/1e3,129u_g[::skip, ::skip], v_g[::skip, ::skip],130speed[::skip, ::skip], cmap='viridis', alpha=0.8)131axes[0, 1].set_xlabel('x (km)')132axes[0, 1].set_ylabel('y (km)')133axes[0, 1].set_title('Geostrophic Velocity Field')134135# Plot 3: Speed magnitude136im3 = axes[1, 0].contourf(X/1e3, Y/1e3, speed*100, levels=20, cmap='plasma')137axes[1, 0].contour(X/1e3, Y/1e3, eta*100, levels=10, colors='white', linewidths=0.5)138axes[1, 0].set_xlabel('x (km)')139axes[1, 0].set_ylabel('y (km)')140axes[1, 0].set_title('Current Speed (cm/s)')141plt.colorbar(im3, ax=axes[1, 0])142143# Plot 4: Cross-section of velocity144mid_idx = ny // 2145axes[1, 1].plot(x/1e3, u_g[mid_idx, :]*100, 'b-', label='u (E-W)', linewidth=2)146axes[1, 1].plot(x/1e3, v_g[mid_idx, :]*100, 'r-', label='v (N-S)', linewidth=2)147axes[1, 1].axhline(y=0, color='k', linestyle='--', linewidth=0.5)148axes[1, 1].set_xlabel('x (km)')149axes[1, 1].set_ylabel('Velocity (cm/s)')150axes[1, 1].set_title(f'Cross-section at y = {y[mid_idx]/1e3:.0f} km')151axes[1, 1].legend()152axes[1, 1].grid(True, alpha=0.3)153154plt.tight_layout()155save_plot('geostrophic_flow.pdf', 'Geostrophic flow around a mesoscale eddy')156157max_speed = np.max(speed) * 100158\end{pycode}159160\section{Ekman Spiral}161162\begin{pycode}163# Ekman layer parameters164A_v = 0.01 # Vertical eddy viscosity (m^2/s)165D_E = np.sqrt(2 * A_v / f) # Ekman depth166167# Wind stress168tau_x = 0.1 # N/m^2 (eastward wind)169tau_y = 0.0170171# Surface velocity172V_0 = tau_x / (rho_0 * np.sqrt(A_v * f / 2))173174# Depth array175z = np.linspace(0, -5 * D_E, 100)176177# Ekman spiral velocities178u_ekman = V_0 * np.exp(z / D_E) * np.cos(np.pi/4 + z/D_E)179v_ekman = V_0 * np.exp(z / D_E) * np.sin(np.pi/4 + z/D_E)180181# Ekman transport182M_E_x = -tau_y / (rho_0 * f)183M_E_y = tau_x / (rho_0 * f)184185# Visualization186fig, axes = plt.subplots(2, 2, figsize=(12, 10))187188# Plot 1: Ekman spiral hodograph189axes[0, 0].plot(u_ekman*100, v_ekman*100, 'b-', linewidth=2)190axes[0, 0].scatter([u_ekman[0]*100], [v_ekman[0]*100], c='red', s=100, zorder=5, label='Surface')191axes[0, 0].scatter([0], [0], c='black', s=50, marker='x')192axes[0, 0].arrow(0, 0, 5, 0, head_width=0.3, head_length=0.2, fc='green', ec='green', label='Wind')193axes[0, 0].set_xlabel('u (cm/s)')194axes[0, 0].set_ylabel('v (cm/s)')195axes[0, 0].set_title('Ekman Spiral Hodograph')196axes[0, 0].axis('equal')197axes[0, 0].grid(True, alpha=0.3)198axes[0, 0].legend()199200# Plot 2: Velocity profiles with depth201axes[0, 1].plot(u_ekman*100, z, 'b-', label='u', linewidth=2)202axes[0, 1].plot(v_ekman*100, z, 'r-', label='v', linewidth=2)203axes[0, 1].axvline(x=0, color='k', linestyle='--', linewidth=0.5)204axes[0, 1].axhline(y=-D_E, color='gray', linestyle=':', label=f'$D_E$ = {D_E:.1f} m')205axes[0, 1].set_xlabel('Velocity (cm/s)')206axes[0, 1].set_ylabel('Depth (m)')207axes[0, 1].set_title('Ekman Velocity Profiles')208axes[0, 1].legend()209axes[0, 1].grid(True, alpha=0.3)210211# Plot 3: 3D Ekman spiral212from mpl_toolkits.mplot3d import Axes3D213ax3d = fig.add_subplot(2, 2, 3, projection='3d')214ax3d.plot(u_ekman*100, v_ekman*100, z, 'b-', linewidth=2)215ax3d.scatter([u_ekman[0]*100], [v_ekman[0]*100], [z[0]], c='red', s=100)216ax3d.set_xlabel('u (cm/s)')217ax3d.set_ylabel('v (cm/s)')218ax3d.set_zlabel('Depth (m)')219ax3d.set_title('3D Ekman Spiral')220221# Plot 4: Cumulative transport222cumulative_u = np.cumsum(u_ekman) * np.abs(z[1] - z[0])223cumulative_v = np.cumsum(v_ekman) * np.abs(z[1] - z[0])224axes[1, 1].plot(cumulative_u, z, 'b-', label='$M_x$', linewidth=2)225axes[1, 1].plot(cumulative_v, z, 'r-', label='$M_y$', linewidth=2)226axes[1, 1].axvline(x=M_E_y, color='r', linestyle='--', alpha=0.5, label=f'$M_E$ = {M_E_y:.2f}')227axes[1, 1].set_xlabel('Cumulative Transport (m$^2$/s)')228axes[1, 1].set_ylabel('Depth (m)')229axes[1, 1].set_title('Ekman Transport Integration')230axes[1, 1].legend()231axes[1, 1].grid(True, alpha=0.3)232233plt.tight_layout()234save_plot('ekman_spiral.pdf', 'Ekman spiral and transport in the surface boundary layer')235\end{pycode}236237\section{Sverdrup Transport}238239\begin{pycode}240# Basin setup for Sverdrup calculation241Lx = 6000e3 # Basin width (m)242Ly = 3000e3 # Basin length (m)243nx, ny = 100, 50244245x_s = np.linspace(0, Lx, nx)246y_s = np.linspace(0, Ly, ny)247X_s, Y_s = np.meshgrid(x_s, y_s)248249# Latitude range (20-50 degrees N)250lat_s = 20 + 30 * Y_s / Ly251f_s = 2 * Omega * np.sin(np.radians(lat_s))252beta_s = 2 * Omega * np.cos(np.radians(lat_s)) / 6.371e6253254# Wind stress (idealized westerlies/trades)255tau_x_s = -0.1 * np.cos(2 * np.pi * Y_s / Ly) # Sinusoidal wind pattern256tau_y_s = np.zeros_like(X_s)257258# Wind stress curl259dtau_x_dy = np.gradient(tau_x_s, y_s[1] - y_s[0], axis=0)260curl_tau = -dtau_x_dy # For tau_y = 0261262# Sverdrup transport263V_sv = curl_tau / (rho_0 * beta_s)264265# Integrate from eastern boundary to get stream function266psi = np.zeros_like(V_sv)267for i in range(nx-2, -1, -1):268psi[:, i] = psi[:, i+1] - V_sv[:, i] * (x_s[1] - x_s[0])269270# Calculate interior velocities271u_sv = -np.gradient(psi, y_s[1] - y_s[0], axis=0)272v_sv = np.gradient(psi, x_s[1] - x_s[0], axis=1)273274fig, axes = plt.subplots(2, 2, figsize=(12, 10))275276# Plot 1: Wind stress pattern277im1 = axes[0, 0].contourf(X_s/1e3, Y_s/1e3, tau_x_s, levels=20, cmap='RdBu_r')278axes[0, 0].set_xlabel('x (km)')279axes[0, 0].set_ylabel('y (km)')280axes[0, 0].set_title('Zonal Wind Stress (N/m$^2$)')281plt.colorbar(im1, ax=axes[0, 0])282283# Plot 2: Wind stress curl284im2 = axes[0, 1].contourf(X_s/1e3, Y_s/1e3, curl_tau*1e7, levels=20, cmap='RdBu_r')285axes[0, 1].set_xlabel('x (km)')286axes[0, 1].set_ylabel('y (km)')287axes[0, 1].set_title('Wind Stress Curl ($\\times 10^{-7}$ N/m$^3$)')288plt.colorbar(im2, ax=axes[0, 1])289290# Plot 3: Sverdrup stream function291levels = np.linspace(psi.min(), psi.max(), 20)292im3 = axes[1, 0].contourf(X_s/1e3, Y_s/1e3, psi/1e6, levels=20, cmap='coolwarm')293cs = axes[1, 0].contour(X_s/1e3, Y_s/1e3, psi/1e6, levels=15, colors='black', linewidths=0.5)294axes[1, 0].set_xlabel('x (km)')295axes[1, 0].set_ylabel('y (km)')296axes[1, 0].set_title('Stream Function (Sv)')297plt.colorbar(im3, ax=axes[1, 0])298299# Plot 4: Meridional transport300axes[1, 1].plot(y_s/1e3, V_sv[:, nx//2]/1e6, 'b-', linewidth=2)301axes[1, 1].axhline(y=0, color='k', linestyle='--', linewidth=0.5)302axes[1, 1].set_xlabel('y (km)')303axes[1, 1].set_ylabel('V (Sv/m)')304axes[1, 1].set_title('Sverdrup Meridional Transport')305axes[1, 1].grid(True, alpha=0.3)306307plt.tight_layout()308save_plot('sverdrup_transport.pdf', 'Sverdrup wind-driven circulation')309310max_transport = np.max(np.abs(psi)) / 1e6311\end{pycode}312313\section{Western Boundary Current}314315\begin{pycode}316# Munk layer solution for western boundary current317# Simplified 1D model318319# Parameters320A_H = 1e4 # Horizontal eddy viscosity (m^2/s)321H = 1000 # Layer depth (m)322L_basin = 5000e3 # Basin width (m)323324# Munk layer width325delta_M = (A_H / beta)**0.333326327# Interior Sverdrup transport (constant for simplicity)328psi_I = 30e6 # 30 Sv329330# Distance from western boundary331x_wb = np.linspace(0, 500e3, 500)332333# Munk solution (normalized)334xi = x_wb / delta_M335psi_wb = psi_I * (1 - np.exp(-xi/2) * (np.cos(np.sqrt(3)/2 * xi) +3361/np.sqrt(3) * np.sin(np.sqrt(3)/2 * xi)))337338# Velocity in western boundary layer339v_wb = np.gradient(psi_wb, x_wb[1] - x_wb[0]) / H340341# Also compute Stommel solution (bottom friction)342r = 1e-7 # Bottom friction coefficient343delta_S = r / beta344psi_stommel = psi_I * (1 - np.exp(-x_wb / delta_S))345v_stommel = np.gradient(psi_stommel, x_wb[1] - x_wb[0]) / H346347fig, axes = plt.subplots(2, 2, figsize=(12, 10))348349# Plot 1: Stream function comparison350axes[0, 0].plot(x_wb/1e3, psi_wb/1e6, 'b-', linewidth=2, label='Munk')351axes[0, 0].plot(x_wb/1e3, psi_stommel/1e6, 'r--', linewidth=2, label='Stommel')352axes[0, 0].axhline(y=psi_I/1e6, color='gray', linestyle=':', label='Interior')353axes[0, 0].axvline(x=delta_M/1e3, color='b', linestyle=':', alpha=0.5)354axes[0, 0].axvline(x=delta_S/1e3, color='r', linestyle=':', alpha=0.5)355axes[0, 0].set_xlabel('Distance from western boundary (km)')356axes[0, 0].set_ylabel('$\\psi$ (Sv)')357axes[0, 0].set_title('Western Boundary Current Structure')358axes[0, 0].legend()359axes[0, 0].grid(True, alpha=0.3)360361# Plot 2: Velocity profiles362axes[0, 1].plot(x_wb/1e3, v_wb*100, 'b-', linewidth=2, label='Munk')363axes[0, 1].plot(x_wb/1e3, v_stommel*100, 'r--', linewidth=2, label='Stommel')364axes[0, 1].set_xlabel('Distance from western boundary (km)')365axes[0, 1].set_ylabel('v (cm/s)')366axes[0, 1].set_title('Northward Velocity')367axes[0, 1].legend()368axes[0, 1].grid(True, alpha=0.3)369axes[0, 1].set_xlim(0, 200)370371# Plot 3: 2D gyre circulation (simplified)372nx_gyre, ny_gyre = 100, 50373x_gyre = np.linspace(0, L_basin, nx_gyre)374y_gyre = np.linspace(0, Ly, ny_gyre)375X_gyre, Y_gyre = np.meshgrid(x_gyre, y_gyre)376377# Simple subtropical gyre378psi_gyre = psi_I * np.sin(np.pi * Y_gyre / Ly)379# Western intensification380xi_gyre = X_gyre / delta_M381wb_factor = 1 - np.exp(-xi_gyre/2) * (np.cos(np.sqrt(3)/2 * xi_gyre) +3821/np.sqrt(3) * np.sin(np.sqrt(3)/2 * xi_gyre))383wb_factor = np.clip(wb_factor, 0, 1)384psi_gyre = psi_gyre * wb_factor385386im3 = axes[1, 0].contourf(X_gyre/1e3, Y_gyre/1e3, psi_gyre/1e6, levels=20, cmap='coolwarm')387cs = axes[1, 0].contour(X_gyre/1e3, Y_gyre/1e3, psi_gyre/1e6, levels=10, colors='black', linewidths=0.5)388axes[1, 0].set_xlabel('x (km)')389axes[1, 0].set_ylabel('y (km)')390axes[1, 0].set_title('Subtropical Gyre with Western Intensification')391plt.colorbar(im3, ax=axes[1, 0], label='$\\psi$ (Sv)')392393# Plot 4: Cross-basin transport profile394mid_y = ny_gyre // 2395axes[1, 1].plot(x_gyre/1e3, psi_gyre[mid_y, :]/1e6, 'b-', linewidth=2)396axes[1, 1].axvline(x=delta_M/1e3, color='r', linestyle='--',397label=f'$\\delta_M$ = {delta_M/1e3:.0f} km')398axes[1, 1].set_xlabel('x (km)')399axes[1, 1].set_ylabel('$\\psi$ (Sv)')400axes[1, 1].set_title('Cross-basin Transport Profile')401axes[1, 1].legend()402axes[1, 1].grid(True, alpha=0.3)403404plt.tight_layout()405save_plot('western_boundary.pdf', 'Western boundary current dynamics')406407max_wbc_vel = np.max(v_wb) * 100408\end{pycode}409410\section{Results Summary}411412\subsection{Geostrophic Flow Statistics}413\begin{pycode}414print(r'\begin{table}[htbp]')415print(r'\centering')416print(r'\caption{Geostrophic flow around mesoscale eddy}')417print(r'\begin{tabular}{lr}')418print(r'\toprule')419print(r'Parameter & Value \\')420print(r'\midrule')421print(f"Maximum SSH anomaly & {np.max(eta)*100:.1f} cm \\\\")422print(f"Maximum current speed & {max_speed:.1f} cm/s \\\\")423print(f"Coriolis parameter & {f:.2e} s$^{{-1}}$ \\\\")424print(f"Eddy radius & 200 km \\\\")425print(r'\bottomrule')426print(r'\end{tabular}')427print(r'\end{table}')428\end{pycode}429430\subsection{Ekman Layer Parameters}431\begin{pycode}432print(r'\begin{table}[htbp]')433print(r'\centering')434print(r'\caption{Ekman spiral characteristics}')435print(r'\begin{tabular}{lr}')436print(r'\toprule')437print(r'Parameter & Value \\')438print(r'\midrule')439print(f"Ekman depth & {D_E:.1f} m \\\\")440print(f"Surface velocity & {V_0*100:.1f} cm/s \\\\")441print(f"Wind stress & {tau_x:.2f} N/m$^2$ \\\\")442print(f"Ekman transport & {M_E_y:.2f} m$^2$/s \\\\")443print(f"Surface deflection & 45$^\\circ$ \\\\")444print(r'\bottomrule')445print(r'\end{tabular}')446print(r'\end{table}')447\end{pycode}448449\subsection{Gyre Circulation}450\begin{pycode}451print(r'\begin{table}[htbp]')452print(r'\centering')453print(r'\caption{Wind-driven gyre parameters}')454print(r'\begin{tabular}{lr}')455print(r'\toprule')456print(r'Parameter & Value \\')457print(r'\midrule')458print(f"Maximum transport & {max_transport:.1f} Sv \\\\")459print(f"Munk layer width & {delta_M/1e3:.0f} km \\\\")460print(f"Stommel layer width & {delta_S/1e3:.0f} km \\\\")461print(f"Max WBC velocity & {max_wbc_vel:.1f} cm/s \\\\")462print(f"Basin width & {L_basin/1e3:.0f} km \\\\")463print(r'\bottomrule')464print(r'\end{tabular}')465print(r'\end{table}')466\end{pycode}467468\subsection{Physical Summary}469\begin{itemize}470\item Ekman depth: \py{f"{D_E:.1f}"} m471\item Munk boundary layer: \py{f"{delta_M/1e3:.0f}"} km472\item Maximum gyre transport: \py{f"{max_transport:.1f}"} Sv473\item WBC velocity: \py{f"{max_wbc_vel:.1f}"} cm/s474\end{itemize}475476\section{Conclusion}477This template demonstrates the fundamental dynamics of ocean circulation. Geostrophic balance governs large-scale flow, with velocities determined by pressure gradients and Earth's rotation. The Ekman spiral shows how wind stress drives surface currents deflected from the wind direction. Sverdrup theory relates interior transport to wind stress curl, while western boundary current intensification results from the need to close the gyre circulation and conserve vorticity.478479\end{document}480481482