Path: blob/main/latex-templates/templates/oceanography/wave_dynamics.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 Wave Dynamics: Dispersion, Spectra, and Coastal Processes}10\author{Physical Oceanography Templates}11\date{\today}1213\begin{document}14\maketitle1516\section{Introduction}17Ocean surface waves transport energy across vast distances and undergo transformations as they approach coastlines. This template covers wave dispersion relations, spectral representations, shoaling, and refraction processes.1819\section{Mathematical Framework}2021\subsection{Linear Wave Theory}22The surface elevation for a monochromatic wave:23\begin{equation}24\eta(x, t) = a \cos(kx - \omega t)25\end{equation}2627\subsection{Dispersion Relation}28Deep water waves satisfy:29\begin{equation}30\omega^2 = gk \tanh(kh)31\end{equation}32In deep water ($kh \gg 1$): $\omega^2 = gk$, giving phase speed $c = \sqrt{g/k}$.3334In shallow water ($kh \ll 1$): $\omega^2 = gk^2h$, giving $c = \sqrt{gh}$.3536\subsection{Group Velocity}37Energy propagates at the group velocity:38\begin{equation}39c_g = \frac{\partial \omega}{\partial k} = \frac{c}{2}\left(1 + \frac{2kh}{\sinh(2kh)}\right)40\end{equation}4142\subsection{Pierson-Moskowitz Spectrum}43Fully developed sea spectrum:44\begin{equation}45S(\omega) = \frac{\alpha g^2}{\omega^5} \exp\left[-\beta\left(\frac{\omega_0}{\omega}\right)^4\right]46\end{equation}47where $\alpha = 8.1 \times 10^{-3}$, $\beta = 0.74$, and $\omega_0 = g/U_{19.5}$.4849\subsection{JONSWAP Spectrum}50Fetch-limited spectrum with peak enhancement:51\begin{equation}52S_J(\omega) = S_{PM}(\omega) \cdot \gamma^{\exp\left[-\frac{(\omega - \omega_p)^2}{2\sigma^2\omega_p^2}\right]}53\end{equation}5455\subsection{Shoaling and Refraction}56Wave height transformation during shoaling:57\begin{equation}58\frac{H}{H_0} = \sqrt{\frac{c_{g0}}{c_g}} = K_s59\end{equation}6061Snell's law for wave refraction:62\begin{equation}63\frac{\sin\theta}{c} = \frac{\sin\theta_0}{c_0}64\end{equation}6566\section{Environment Setup}6768\begin{pycode}69import numpy as np70import matplotlib.pyplot as plt71from scipy.optimize import fsolve7273plt.rc('text', usetex=True)74plt.rc('font', family='serif')75np.random.seed(42)7677def save_plot(filename, caption=""):78plt.savefig(filename, bbox_inches='tight', dpi=150)79print(r'\begin{figure}[htbp]')80print(r'\centering')81print(r'\includegraphics[width=0.9\textwidth]{' + filename + '}')82if caption:83print(r'\caption{' + caption + '}')84print(r'\end{figure}')85plt.close()8687# Physical constants88g = 9.81 # Gravity (m/s^2)89\end{pycode}9091\section{Dispersion Relation Analysis}9293\begin{pycode}94def dispersion(omega, h):95"""Solve dispersion relation for wavenumber k"""96def equation(k):97return omega**2 - g * k * np.tanh(k * h)9899k_deep = omega**2 / g # Deep water approximation100k = fsolve(equation, k_deep)[0]101return k102103def phase_speed(k, h):104"""Phase speed from wavenumber and depth"""105return np.sqrt(g / k * np.tanh(k * h))106107def group_speed(k, h):108"""Group velocity"""109c = phase_speed(k, h)110n = 0.5 * (1 + 2*k*h / np.sinh(2*k*h))111return n * c112113# Calculate dispersion for various periods114periods = np.array([4, 6, 8, 10, 12, 15, 20]) # seconds115omega_vals = 2 * np.pi / periods116depths = np.linspace(5, 500, 100) # meters117118# Store results119wavelengths = np.zeros((len(periods), len(depths)))120phase_speeds = np.zeros((len(periods), len(depths)))121group_speeds = np.zeros((len(periods), len(depths)))122123for i, omega in enumerate(omega_vals):124for j, h in enumerate(depths):125k = dispersion(omega, h)126wavelengths[i, j] = 2 * np.pi / k127phase_speeds[i, j] = phase_speed(k, h)128group_speeds[i, j] = group_speed(k, h)129130fig, axes = plt.subplots(2, 2, figsize=(12, 10))131132# Plot 1: Wavelength vs depth133for i, T in enumerate(periods):134axes[0, 0].plot(depths, wavelengths[i, :], label=f'T={T}s')135axes[0, 0].set_xlabel('Water Depth (m)')136axes[0, 0].set_ylabel('Wavelength (m)')137axes[0, 0].set_title('Wavelength vs Depth')138axes[0, 0].legend(fontsize=8)139axes[0, 0].grid(True, alpha=0.3)140axes[0, 0].set_xscale('log')141142# Plot 2: Phase speed vs depth143for i, T in enumerate(periods):144axes[0, 1].plot(depths, phase_speeds[i, :], label=f'T={T}s')145# Deep water limit146axes[0, 1].axhline(y=np.sqrt(g * wavelengths[-1, -1] / (2*np.pi)),147color='k', linestyle='--', alpha=0.5)148axes[0, 1].set_xlabel('Water Depth (m)')149axes[0, 1].set_ylabel('Phase Speed (m/s)')150axes[0, 1].set_title('Phase Speed vs Depth')151axes[0, 1].legend(fontsize=8)152axes[0, 1].grid(True, alpha=0.3)153axes[0, 1].set_xscale('log')154155# Plot 3: Group speed / Phase speed ratio156for i, T in enumerate(periods):157ratio = group_speeds[i, :] / phase_speeds[i, :]158axes[1, 0].plot(depths, ratio, label=f'T={T}s')159axes[1, 0].axhline(y=0.5, color='k', linestyle='--', alpha=0.5, label='Deep water')160axes[1, 0].axhline(y=1.0, color='k', linestyle=':', alpha=0.5, label='Shallow water')161axes[1, 0].set_xlabel('Water Depth (m)')162axes[1, 0].set_ylabel('$c_g / c$')163axes[1, 0].set_title('Group Speed / Phase Speed Ratio')164axes[1, 0].legend(fontsize=8)165axes[1, 0].grid(True, alpha=0.3)166axes[1, 0].set_xscale('log')167168# Plot 4: Relative depth parameter kh169for i, T in enumerate(periods):170k = 2 * np.pi / wavelengths[i, :]171kh = k * depths172axes[1, 1].plot(depths, kh, label=f'T={T}s')173axes[1, 1].axhline(y=np.pi, color='k', linestyle='--', alpha=0.5, label='Deep water limit')174axes[1, 1].axhline(y=0.3, color='k', linestyle=':', alpha=0.5, label='Shallow water limit')175axes[1, 1].set_xlabel('Water Depth (m)')176axes[1, 1].set_ylabel('kh')177axes[1, 1].set_title('Relative Depth Parameter')178axes[1, 1].legend(fontsize=8)179axes[1, 1].grid(True, alpha=0.3)180axes[1, 1].set_xscale('log')181axes[1, 1].set_yscale('log')182183plt.tight_layout()184save_plot('dispersion_relations.pdf', 'Wave dispersion characteristics')185\end{pycode}186187\section{Wave Spectra}188189\begin{pycode}190def pierson_moskowitz(omega, U):191"""Pierson-Moskowitz spectrum"""192alpha = 8.1e-3193beta = 0.74194omega_0 = g / U195S = (alpha * g**2 / omega**5) * np.exp(-beta * (omega_0 / omega)**4)196return S197198def jonswap(omega, U, fetch, gamma=3.3):199"""JONSWAP spectrum"""200# Peak frequency201omega_p = 22 * (g**2 / (U * fetch))**0.333202203# PM base spectrum204alpha = 0.076 * (U**2 / (fetch * g))**0.22205S_pm = (alpha * g**2 / omega**5) * np.exp(-1.25 * (omega_p / omega)**4)206207# Peak enhancement208sigma = np.where(omega <= omega_p, 0.07, 0.09)209r = np.exp(-(omega - omega_p)**2 / (2 * sigma**2 * omega_p**2))210S_j = S_pm * gamma**r211212return S_j, omega_p213214# Wave conditions215U = 15 # Wind speed (m/s)216fetch_values = [100e3, 300e3, 1000e3] # Fetch distances (m)217218omega = np.linspace(0.2, 2.5, 200) # Frequency range (rad/s)219220# Calculate spectra221S_pm = pierson_moskowitz(omega, U)222223fig, axes = plt.subplots(2, 2, figsize=(12, 10))224225# Plot 1: Pierson-Moskowitz spectrum226axes[0, 0].plot(omega, S_pm, 'b-', linewidth=2)227axes[0, 0].fill_between(omega, 0, S_pm, alpha=0.3)228axes[0, 0].set_xlabel('$\\omega$ (rad/s)')229axes[0, 0].set_ylabel('$S(\\omega)$ (m$^2$s/rad)')230axes[0, 0].set_title(f'Pierson-Moskowitz Spectrum (U={U} m/s)')231axes[0, 0].grid(True, alpha=0.3)232233# Plot 2: JONSWAP spectra for different fetches234colors = ['blue', 'green', 'red']235for i, fetch in enumerate(fetch_values):236S_j, omega_p = jonswap(omega, U, fetch)237label = f'Fetch = {fetch/1e3:.0f} km'238axes[0, 1].plot(omega, S_j, color=colors[i], linewidth=2, label=label)239240axes[0, 1].set_xlabel('$\\omega$ (rad/s)')241axes[0, 1].set_ylabel('$S(\\omega)$ (m$^2$s/rad)')242axes[0, 1].set_title('JONSWAP Spectra')243axes[0, 1].legend()244axes[0, 1].grid(True, alpha=0.3)245246# Plot 3: Significant wave height vs fetch247fetches = np.linspace(50e3, 1000e3, 50)248Hs_values = []249Tp_values = []250251for fetch in fetches:252S_j, omega_p = jonswap(omega, U, fetch)253# Significant wave height: Hs = 4 * sqrt(m0)254m0 = np.trapz(S_j, omega)255Hs = 4 * np.sqrt(m0)256Tp = 2 * np.pi / omega_p257Hs_values.append(Hs)258Tp_values.append(Tp)259260axes[1, 0].plot(fetches/1e3, Hs_values, 'b-', linewidth=2)261axes[1, 0].set_xlabel('Fetch (km)')262axes[1, 0].set_ylabel('$H_s$ (m)')263axes[1, 0].set_title(f'Significant Wave Height Growth (U={U} m/s)')264axes[1, 0].grid(True, alpha=0.3)265266# Plot 4: Peak period vs fetch267axes[1, 1].plot(fetches/1e3, Tp_values, 'r-', linewidth=2)268axes[1, 1].set_xlabel('Fetch (km)')269axes[1, 1].set_ylabel('$T_p$ (s)')270axes[1, 1].set_title('Peak Period Growth')271axes[1, 1].grid(True, alpha=0.3)272273plt.tight_layout()274save_plot('wave_spectra.pdf', 'Ocean wave spectra and wave growth')275276# Store for later use277final_Hs = Hs_values[-1]278final_Tp = Tp_values[-1]279\end{pycode}280281\section{Random Sea Surface Simulation}282283\begin{pycode}284# Generate random sea surface from spectrum285def generate_sea_surface(S, omega, x, t):286"""Generate sea surface elevation from spectrum"""287domega = omega[1] - omega[0]288amplitudes = np.sqrt(2 * S * domega)289phases = np.random.uniform(0, 2*np.pi, len(omega))290291eta = np.zeros_like(x)292for i, om in enumerate(omega):293k = om**2 / g # Deep water294eta += amplitudes[i] * np.cos(k * x - om * t + phases[i])295296return eta297298# Generate surface299x = np.linspace(0, 1000, 500) # 1 km domain300t_values = [0, 5, 10, 15] # Time snapshots301302# Use JONSWAP spectrum303S_j, omega_p = jonswap(omega, U, 300e3)304305fig, axes = plt.subplots(2, 2, figsize=(12, 10))306307# Plot 1-4: Sea surface at different times308for idx, t in enumerate(t_values):309ax = axes[idx // 2, idx % 2]310eta = generate_sea_surface(S_j, omega, x, t)311ax.plot(x, eta, 'b-', linewidth=1)312ax.fill_between(x, 0, eta, where=(eta > 0), alpha=0.3, color='blue')313ax.fill_between(x, eta, 0, where=(eta < 0), alpha=0.3, color='blue')314ax.axhline(y=0, color='k', linestyle='-', linewidth=0.5)315ax.set_xlabel('x (m)')316ax.set_ylabel('$\\eta$ (m)')317ax.set_title(f't = {t} s')318ax.set_ylim(-5, 5)319ax.grid(True, alpha=0.3)320321plt.tight_layout()322save_plot('sea_surface.pdf', 'Random sea surface time evolution')323\end{pycode}324325\section{Wave Shoaling and Refraction}326327\begin{pycode}328# Shoaling transformation329h_deep = 100 # Deep water depth330h_shallow = np.linspace(100, 2, 100)331332T = 10 # Wave period333omega_wave = 2 * np.pi / T334k_deep = omega_wave**2 / g335L_deep = 2 * np.pi / k_deep336H_deep = 2 # Deep water wave height (m)337338# Calculate shoaling coefficient339Ks_values = []340k_values = []341c_values = []342cg_values = []343344for h in h_shallow:345k = dispersion(omega_wave, h)346c = phase_speed(k, h)347cg = group_speed(k, h)348cg_deep = group_speed(k_deep, h_deep)349350Ks = np.sqrt(cg_deep / cg)351Ks_values.append(Ks)352k_values.append(k)353c_values.append(c)354cg_values.append(cg)355356Ks_values = np.array(Ks_values)357H_shoaled = H_deep * Ks_values358359fig, axes = plt.subplots(2, 2, figsize=(12, 10))360361# Plot 1: Shoaling coefficient362axes[0, 0].plot(h_shallow, Ks_values, 'b-', linewidth=2)363axes[0, 0].set_xlabel('Water Depth (m)')364axes[0, 0].set_ylabel('$K_s$')365axes[0, 0].set_title('Shoaling Coefficient')366axes[0, 0].grid(True, alpha=0.3)367axes[0, 0].invert_xaxis()368369# Plot 2: Wave height transformation370axes[0, 1].plot(h_shallow, H_shoaled, 'r-', linewidth=2)371axes[0, 1].axhline(y=H_deep, color='k', linestyle='--', label=f'$H_0$ = {H_deep} m')372axes[0, 1].set_xlabel('Water Depth (m)')373axes[0, 1].set_ylabel('Wave Height (m)')374axes[0, 1].set_title('Wave Height During Shoaling')375axes[0, 1].legend()376axes[0, 1].grid(True, alpha=0.3)377axes[0, 1].invert_xaxis()378379# Plot 3: Wave refraction380# Beach with varying depth381nx, ny = 100, 50382x_beach = np.linspace(0, 2000, nx)383y_beach = np.linspace(-500, 500, ny)384X_beach, Y_beach = np.meshgrid(x_beach, y_beach)385386# Depth decreases toward shore (linear beach)387h_beach = 50 - 0.025 * X_beach388h_beach = np.maximum(h_beach, 0.5)389390# Calculate wave crests (refraction)391# Waves approaching at angle392theta_deep = np.radians(30) # 30 degree approach angle393394# Calculate local angle using Snell's law395c_deep = np.sqrt(g * h_beach.max())396c_local = np.sqrt(g * h_beach * np.tanh(k_deep * h_beach) / k_deep)397theta_local = np.arcsin(np.clip(c_local / c_deep * np.sin(theta_deep), -1, 1))398399# Wave crest positions400im = axes[1, 0].contourf(X_beach, Y_beach, h_beach, levels=20, cmap='Blues_r')401axes[1, 0].contour(X_beach, Y_beach, h_beach, levels=10, colors='white', linewidths=0.5)402axes[1, 0].set_xlabel('Distance offshore (m)')403axes[1, 0].set_ylabel('Alongshore (m)')404axes[1, 0].set_title('Bathymetry')405plt.colorbar(im, ax=axes[1, 0], label='Depth (m)')406407# Plot 4: Refraction diagram408# Draw wave rays409for y0 in np.linspace(-400, 400, 9):410ray_x = [0]411ray_y = [y0]412x_curr = 0413y_curr = y0414theta_curr = theta_deep415ds = 20 # Step size416417while x_curr < 1900:418# Get local depth419j = int(np.clip(x_curr / x_beach[-1] * (nx-1), 0, nx-1))420h_local = 50 - 0.025 * x_curr421h_local = max(h_local, 0.5)422423# Local phase speed424c_loc = np.sqrt(g * h_local * np.tanh(k_deep * h_local) / k_deep)425426# Update angle (Snell's law)427sin_theta = c_loc / c_deep * np.sin(theta_deep)428if abs(sin_theta) <= 1:429theta_curr = np.arcsin(sin_theta)430else:431break432433# Step forward434x_curr += ds * np.cos(theta_curr)435y_curr += ds * np.sin(theta_curr)436ray_x.append(x_curr)437ray_y.append(y_curr)438439axes[1, 1].plot(ray_x, ray_y, 'b-', linewidth=0.8, alpha=0.7)440441axes[1, 1].contour(X_beach, Y_beach, h_beach, levels=[5, 10, 20, 30, 40],442colors='gray', linewidths=0.5)443axes[1, 1].set_xlabel('Distance offshore (m)')444axes[1, 1].set_ylabel('Alongshore (m)')445axes[1, 1].set_title(f'Wave Refraction (approach angle = {np.degrees(theta_deep):.0f}$^\\circ$)')446axes[1, 1].set_xlim(0, 2000)447axes[1, 1].set_ylim(-500, 500)448449plt.tight_layout()450save_plot('shoaling_refraction.pdf', 'Wave shoaling and refraction')451452max_Hs_shoaled = np.max(H_shoaled)453\end{pycode}454455\section{Breaking Wave Criterion}456457\begin{pycode}458# Wave breaking analysis459fig, axes = plt.subplots(1, 2, figsize=(12, 5))460461# McCowan breaking criterion: H/h = 0.78462# Miche breaking criterion: H/L = 0.142 tanh(kh)463464depths_break = np.linspace(1, 20, 100)465466# Maximum wave height before breaking467Hb_mccowan = 0.78 * depths_break468469# Breaking wave height for different periods470for T_break in [6, 8, 10, 12]:471omega_b = 2 * np.pi / T_break472Hb_miche = []473for h in depths_break:474k = dispersion(omega_b, h)475L = 2 * np.pi / k476H_max = 0.142 * L * np.tanh(k * h)477Hb_miche.append(H_max)478axes[0].plot(depths_break, Hb_miche, label=f'T={T_break}s')479480axes[0].plot(depths_break, Hb_mccowan, 'k--', linewidth=2, label='McCowan')481axes[0].set_xlabel('Water Depth (m)')482axes[0].set_ylabel('Breaking Wave Height (m)')483axes[0].set_title('Breaking Wave Height Limits')484axes[0].legend()485axes[0].grid(True, alpha=0.3)486487# Breaker type parameter488# Surf similarity parameter: xi = tan(beta) / sqrt(H/L)489slopes = np.linspace(0.01, 0.15, 100)490H_break = 2 # m491L_break = 100 # m (approx for T=8s)492493xi = slopes / np.sqrt(H_break / L_break)494495axes[1].plot(slopes, xi, 'b-', linewidth=2)496axes[1].axhline(y=0.5, color='r', linestyle='--', label='Spilling (< 0.5)')497axes[1].axhline(y=3.3, color='g', linestyle='--', label='Surging (> 3.3)')498axes[1].fill_between(slopes, 0.5, 3.3, alpha=0.2, color='yellow', label='Plunging')499axes[1].set_xlabel('Beach Slope')500axes[1].set_ylabel('Surf Similarity $\\xi$')501axes[1].set_title('Breaker Type Classification')502axes[1].legend()503axes[1].grid(True, alpha=0.3)504505plt.tight_layout()506save_plot('wave_breaking.pdf', 'Wave breaking criteria and breaker types')507\end{pycode}508509\section{Results Summary}510511\subsection{Wave Properties}512\begin{pycode}513print(r'\begin{table}[htbp]')514print(r'\centering')515print(r'\caption{Wave characteristics for T=10s}')516print(r'\begin{tabular}{lcc}')517print(r'\toprule')518print(r'Property & Deep Water & Shallow (10m) \\')519print(r'\midrule')520521# Deep water values522L_d = g * 10**2 / (2 * np.pi)523c_d = g * 10 / (2 * np.pi)524cg_d = c_d / 2525526# Shallow water values (h=10m)527k_s = dispersion(omega_wave, 10)528L_s = 2 * np.pi / k_s529c_s = phase_speed(k_s, 10)530cg_s = group_speed(k_s, 10)531532print(f"Wavelength (m) & {L_d:.1f} & {L_s:.1f} \\\\")533print(f"Phase speed (m/s) & {c_d:.1f} & {c_s:.1f} \\\\")534print(f"Group speed (m/s) & {cg_d:.1f} & {cg_s:.1f} \\\\")535print(r'\bottomrule')536print(r'\end{tabular}')537print(r'\end{table}')538\end{pycode}539540\subsection{Spectral Parameters}541\begin{pycode}542print(r'\begin{table}[htbp]')543print(r'\centering')544print(r'\caption{Wave spectrum characteristics}')545print(r'\begin{tabular}{lr}')546print(r'\toprule')547print(r'Parameter & Value \\')548print(r'\midrule')549print(f"Wind speed & {U} m/s \\\\")550print(f"Significant wave height & {final_Hs:.2f} m \\\\")551print(f"Peak period & {final_Tp:.1f} s \\\\")552print(f"Maximum fetch & {fetches[-1]/1e3:.0f} km \\\\")553print(r'\bottomrule')554print(r'\end{tabular}')555print(r'\end{table}')556\end{pycode}557558\subsection{Coastal Transformation}559\begin{pycode}560print(r'\begin{table}[htbp]')561print(r'\centering')562print(r'\caption{Wave transformation during shoaling}')563print(r'\begin{tabular}{lr}')564print(r'\toprule')565print(r'Parameter & Value \\')566print(r'\midrule')567print(f"Deep water height & {H_deep:.1f} m \\\\")568print(f"Maximum shoaled height & {max_Hs_shoaled:.2f} m \\\\")569print(f"Maximum shoaling coefficient & {np.max(Ks_values):.2f} \\\\")570print(f"Refraction angle (deep) & {np.degrees(theta_deep):.0f}$^\\circ$ \\\\")571print(r'\bottomrule')572print(r'\end{tabular}')573print(r'\end{table}')574\end{pycode}575576\subsection{Physical Summary}577\begin{itemize}578\item Deep water wavelength (T=10s): \py{f"{L_d:.1f}"} m579\item JONSWAP $H_s$: \py{f"{final_Hs:.2f}"} m580\item Maximum shoaling coefficient: \py{f"{np.max(Ks_values):.2f}"}581\item Approach angle: \py{f"{np.degrees(theta_deep):.0f}"} degrees582\end{itemize}583584\section{Conclusion}585This template demonstrates fundamental ocean wave dynamics. The dispersion relation governs how wave properties vary with depth, transitioning from deep to shallow water behavior. Spectral representations (Pierson-Moskowitz, JONSWAP) describe the energy distribution in random seas and wave growth with fetch. Shoaling causes wave height to increase as waves approach shore, while refraction bends wave crests to align with depth contours. These processes are essential for coastal engineering and understanding surf zone dynamics.586587\end{document}588589590