Path: blob/main/latex-templates/templates/hydrology/groundwater_flow.tex
75 views
unlisted
% Groundwater Flow Analysis Template1% Topics: Darcy's law, aquifer hydraulics, pumping tests, well hydraulics, numerical methods2% Style: Technical report with computational aquifer analysis34\documentclass[a4paper, 11pt]{article}5\usepackage[utf8]{inputenc}6\usepackage[T1]{fontenc}7\usepackage{amsmath, amssymb}8\usepackage{graphicx}9\usepackage{siunitx}10\usepackage{booktabs}11\usepackage{subcaption}12\usepackage[makestderr]{pythontex}13\usepackage{geometry}14\geometry{margin=1in}15\usepackage{hyperref}16\usepackage{float}1718% Theorem environments19\newtheorem{definition}{Definition}[section]20\newtheorem{theorem}{Theorem}[section]21\newtheorem{example}{Example}[section]22\newtheorem{remark}{Remark}[section]2324\title{Groundwater Flow Analysis: Darcy's Law and Well Hydraulics}25\author{Hydrogeology Research Group}26\date{\today}2728\begin{document}29\maketitle3031\begin{abstract}32This technical report presents comprehensive computational analysis of groundwater flow in33porous media, focusing on aquifer hydraulics and well dynamics. We examine the fundamental34principles of Darcy's law, develop analytical solutions for radial flow to wells using the35Theis equation and Cooper-Jacob approximation, and implement numerical finite difference36methods for solving the 2D groundwater flow equation. Pumping test analysis demonstrates37determination of hydraulic parameters including transmissivity ($T = 1.2 \times 10^{-3}$ m²/s)38and storativity ($S = 2.5 \times 10^{-4}$), with visualization of hydraulic head distributions39and groundwater flow fields under various pumping scenarios.40\end{abstract}4142\section{Introduction}4344Groundwater flow through porous media is governed by Darcy's law, which relates hydraulic45gradient to discharge velocity through the hydraulic conductivity of the formation. Understanding46these flow dynamics is essential for water resource management, contaminant transport modeling,47and wellfield design. The mathematical framework combines conservation of mass with Darcy's48empirical relationship to yield partial differential equations describing hydraulic head49distributions in both steady-state and transient conditions.5051\begin{definition}[Darcy's Law]52For one-dimensional flow through a porous medium, the specific discharge $q$ is proportional53to the hydraulic gradient:54\begin{equation}55q = -K \frac{dh}{dx}56\end{equation}57where $K$ is hydraulic conductivity (m/s) and $h$ is hydraulic head (m). The negative sign58indicates flow from high to low head.59\end{definition}6061\section{Theoretical Framework}6263\subsection{Governing Equation for Groundwater Flow}6465Combining Darcy's law with the continuity equation for incompressible flow yields the governing66equation for transient groundwater flow in a confined aquifer. Consider a representative67elementary volume in an aquifer where water is released from storage as head declines.6869\begin{theorem}[2D Groundwater Flow Equation]70For horizontal flow in a confined aquifer with constant transmissivity, the governing equation is:71\begin{equation}72\frac{\partial^2 h}{\partial x^2} + \frac{\partial^2 h}{\partial y^2} = \frac{S}{T} \frac{\partial h}{\partial t}73\end{equation}74where $T = Kb$ is transmissivity (m²/s), $b$ is aquifer thickness (m), and $S$ is storativity75(dimensionless). For steady-state conditions, this reduces to Laplace's equation.76\end{theorem}7778\subsection{Radial Flow to a Well}7980When a well pumps water from a confined aquifer, flow converges radially toward the well81creating a cone of depression. The Theis solution provides the fundamental analytical framework82for analyzing this transient drawdown response.8384\begin{definition}[Theis Equation]85The drawdown $s$ at radial distance $r$ from a pumping well at time $t$ is:86\begin{equation}87s(r, t) = \frac{Q}{4\pi T} W(u)88\end{equation}89where $Q$ is pumping rate (m³/s), $W(u)$ is the well function (exponential integral), and90$u = r^2 S / (4Tt)$ is the dimensionless time parameter \cite{theis1935relation}.91\end{definition}9293The well function is defined as:94\begin{equation}95W(u) = \int_u^\infty \frac{e^{-y}}{y} dy = -0.5772 - \ln(u) + u - \frac{u^2}{2 \cdot 2!} + \frac{u^3}{3 \cdot 3!} - \cdots96\end{equation}9798\begin{theorem}[Cooper-Jacob Approximation]99For small values of $u$ (large $t$ or small $r$), the series expansion truncates to:100\begin{equation}101s \approx \frac{2.30Q}{4\pi T} \log_{10}\left(\frac{2.25Tt}{r^2 S}\right)102\end{equation}103This provides a straight-line relationship on semi-log plots used for pumping test analysis104\cite{cooper1946drawdown}.105\end{theorem}106107\section{Computational Implementation}108109We now implement computational methods for analyzing groundwater flow, including analytical110solutions for well hydraulics and numerical finite difference approximations for complex111boundary conditions. The analysis begins with establishing fundamental hydraulic properties112and developing functions for the Theis well function calculation.113114\begin{pycode}115import numpy as np116import matplotlib.pyplot as plt117from scipy.special import expi, exp1118from scipy.optimize import curve_fit119from scipy.integrate import odeint120import matplotlib.patches as patches121122np.random.seed(42)123124# Configure matplotlib for LaTeX rendering125plt.rcParams['text.usetex'] = True126plt.rcParams['font.family'] = 'serif'127plt.rcParams['font.size'] = 10128129# Define hydraulic parameters for confined aquifer130K = 1.5e-5 # Hydraulic conductivity (m/s)131b = 25.0 # Aquifer thickness (m)132T = K * b # Transmissivity (m^2/s)133S = 2.5e-4 # Storativity (dimensionless)134Q = 0.015 # Pumping rate (m^3/s = 15 L/s)135136def well_function(u):137"""138Theis well function W(u) = exponential integral.139For small u, use series expansion for numerical stability.140"""141u = np.asarray(u)142w = np.zeros_like(u, dtype=float)143144# For u < 0.01, use series expansion145small_u = u < 0.01146if np.any(small_u):147u_small = u[small_u]148w[small_u] = -0.5772 - np.log(u_small) + u_small - u_small**2/(2*2) + u_small**3/(3*6)149150# For larger u, use exponential integral151large_u = ~small_u152if np.any(large_u):153w[large_u] = -expi(-u[large_u])154155return w156157def theis_drawdown(r, t, Q, T, S):158"""159Calculate drawdown using Theis equation.160161Parameters:162r: radial distance from well (m)163t: time since pumping started (s)164Q: pumping rate (m^3/s)165T: transmissivity (m^2/s)166S: storativity (dimensionless)167168Returns:169s: drawdown (m)170"""171u = r**2 * S / (4 * T * t)172w = well_function(u)173s = Q / (4 * np.pi * T) * w174return s175176def cooper_jacob_drawdown(r, t, Q, T, S):177"""178Cooper-Jacob approximation for large t, small r.179Valid when u < 0.01.180"""181s = 2.30 * Q / (4 * np.pi * T) * np.log10(2.25 * T * t / (r**2 * S))182return s183184# Radial distances for analysis185r_obs = np.array([10, 25, 50, 100, 200, 300]) # observation wells (m)186time_pumping = np.logspace(1, 5, 100) # 10 s to ~1 day187188# Calculate drawdown at multiple observation points189drawdown_curves = {}190for r in r_obs:191drawdown_curves[r] = theis_drawdown(r, time_pumping, Q, T, S)192\end{pycode}193194\subsection{Drawdown Analysis at Multiple Observation Wells}195196Pumping test analysis relies on measuring drawdown at multiple observation wells located at197varying radial distances from the pumping well. These time-drawdown curves reveal the transient198response of the aquifer and enable determination of hydraulic properties through curve matching199or straight-line methods. The following analysis examines drawdown evolution over time periods200ranging from minutes to over 24 hours of continuous pumping.201202\begin{pycode}203# Create drawdown vs time plots204fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13, 5))205206# Plot 1: Time-drawdown curves (semi-log)207colors = plt.cm.viridis(np.linspace(0, 0.9, len(r_obs)))208for i, r in enumerate(r_obs):209ax1.semilogx(time_pumping / 3600, drawdown_curves[r],210linewidth=2.5, color=colors[i], label=f'$r = {r}$ m')211ax1.set_xlabel('Time (hours)', fontsize=11)212ax1.set_ylabel('Drawdown (m)', fontsize=11)213ax1.set_title('Time-Drawdown Response at Observation Wells', fontsize=12, fontweight='bold')214ax1.legend(loc='lower right', fontsize=9)215ax1.grid(True, alpha=0.3, which='both')216ax1.set_xlim(time_pumping[0]/3600, time_pumping[-1]/3600)217218# Plot 2: Distance-drawdown at fixed times219time_snapshots = np.array([1, 6, 24]) * 3600 # 1, 6, 24 hours in seconds220r_fine = np.logspace(0.5, 2.8, 200) # 3 m to 600 m221222colors_time = ['red', 'blue', 'green']223for i, t_snap in enumerate(time_snapshots):224s_snap = theis_drawdown(r_fine, t_snap, Q, T, S)225ax2.semilogx(r_fine, s_snap, linewidth=2.5, color=colors_time[i],226label=f'$t = {int(t_snap/3600)}$ hr')227ax2.set_xlabel('Distance from well (m)', fontsize=11)228ax2.set_ylabel('Drawdown (m)', fontsize=11)229ax2.set_title('Radial Drawdown Distribution', fontsize=12, fontweight='bold')230ax2.legend(loc='upper right', fontsize=9)231ax2.grid(True, alpha=0.3, which='both')232ax2.invert_yaxis()233234plt.tight_layout()235plt.savefig('groundwater_flow_drawdown.pdf', dpi=150, bbox_inches='tight')236plt.close()237\end{pycode}238239\begin{figure}[H]240\centering241\includegraphics[width=\textwidth]{groundwater_flow_drawdown.pdf}242\caption{Drawdown analysis for pumping test in confined aquifer with $T = 3.75 \times 10^{-4}$ m²/s243and $S = 2.5 \times 10^{-4}$. Left panel shows time-drawdown curves at six observation wells244(10--300 m from pumping well), demonstrating the characteristic logarithmic increase in drawdown245with time. Right panel displays radial drawdown profiles at 1, 6, and 24 hours, illustrating246the expanding cone of depression. Maximum drawdown at the 10-m observation well reaches247approximately 2.8 m after 24 hours of pumping at 15 L/s. The semi-logarithmic plotting reveals248the straight-line behavior predicted by the Cooper-Jacob approximation at late times.}249\end{figure}250251\section{Cooper-Jacob Analysis for Parameter Estimation}252253The Cooper-Jacob method provides a practical approach for determining transmissivity and254storativity from pumping test data without requiring curve matching to type curves. By plotting255drawdown versus the logarithm of time, the late-time data forms a straight line whose slope256and intercept yield the aquifer parameters. This graphical method is widely used in field257hydrogeology for its simplicity and computational efficiency.258259\begin{pycode}260# Generate synthetic pumping test data with measurement noise261t_measured = np.array([1, 2, 3, 5, 8, 10, 15, 20, 30, 45, 60, 90, 120, 180,262300, 480, 720, 1080, 1440, 2160, 4320, 8640, 14400]) * 60 # minutes to seconds263r_test_well = 50 # observation well at 50 m264s_measured = theis_drawdown(r_test_well, t_measured, Q, T, S)265# Add realistic measurement noise266noise_std = 0.02 # 2 cm standard deviation267s_measured_noisy = s_measured + np.random.normal(0, noise_std, len(s_measured))268269# Cooper-Jacob analysis: plot s vs log10(t)270fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13, 5))271272# Semi-log plot for Cooper-Jacob273ax1.plot(t_measured / 3600, s_measured_noisy, 'bo', markersize=8,274markeredgecolor='black', markeredgewidth=1, label='Field measurements')275276# Fit straight line to late-time data (Cooper-Jacob approximation valid for u < 0.01)277u_values = r_test_well**2 * S / (4 * T * t_measured)278late_time_idx = u_values < 0.01279t_late = t_measured[late_time_idx]280s_late = s_measured_noisy[late_time_idx]281282# Linear fit to log(t) vs s283log10_t_late = np.log10(t_late)284coeffs = np.polyfit(log10_t_late, s_late, 1)285slope_cj = coeffs[0] # m per log cycle286intercept_cj = coeffs[1]287288# Calculate T and S from slope and intercept289T_estimated = 2.30 * Q / (4 * np.pi * slope_cj)290# From intercept: t_0 is where s = 0291t_0 = 10**(-intercept_cj / slope_cj)292S_estimated = 2.25 * T_estimated * t_0 / r_test_well**2293294# Plot fitted line295t_fit = np.logspace(np.log10(t_late[0]), np.log10(t_measured[-1]), 50)296s_fit = slope_cj * np.log10(t_fit) + intercept_cj297ax1.semilogx(t_fit / 3600, s_fit, 'r-', linewidth=2.5, label='Cooper-Jacob fit')298ax1.set_xlabel('Time (hours)', fontsize=11)299ax1.set_ylabel('Drawdown (m)', fontsize=11)300ax1.set_title('Cooper-Jacob Semi-Log Analysis', fontsize=12, fontweight='bold')301ax1.legend(loc='lower right', fontsize=9)302ax1.grid(True, alpha=0.3, which='both')303ax1.text(0.05, 0.95, f'$\\Delta s$ per log cycle = {slope_cj:.3f} m\\n$T$ = {T_estimated:.2e} m$^2$/s\\n$S$ = {S_estimated:.2e}',304transform=ax1.transAxes, fontsize=9, verticalalignment='top',305bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))306307# Residual analysis308s_predicted = slope_cj * np.log10(t_measured) + intercept_cj309residuals = s_measured_noisy - s_predicted310ax2.plot(t_measured / 3600, residuals * 100, 'go', markersize=8,311markeredgecolor='black', markeredgewidth=1)312ax2.axhline(y=0, color='red', linestyle='--', linewidth=2)313ax2.axhline(y=noise_std*100, color='gray', linestyle=':', linewidth=1.5, alpha=0.7)314ax2.axhline(y=-noise_std*100, color='gray', linestyle=':', linewidth=1.5, alpha=0.7)315ax2.set_xlabel('Time (hours)', fontsize=11)316ax2.set_ylabel('Residual (cm)', fontsize=11)317ax2.set_title('Cooper-Jacob Fit Residuals', fontsize=12, fontweight='bold')318ax2.grid(True, alpha=0.3)319ax2.set_xscale('log')320321plt.tight_layout()322plt.savefig('groundwater_flow_cooper_jacob.pdf', dpi=150, bbox_inches='tight')323plt.close()324\end{pycode}325326\begin{figure}[H]327\centering328\includegraphics[width=\textwidth]{groundwater_flow_cooper_jacob.pdf}329\caption{Cooper-Jacob straight-line analysis of pumping test data from observation well at 50 m330distance. Left panel shows drawdown measurements (blue circles) plotted versus logarithm of time,331with the fitted straight line (red) representing the Cooper-Jacob approximation. The slope of332\py{f'{slope_cj:.3f}'} m per log cycle yields transmissivity $T = \py{f'{T_estimated:.2e}'}$ m²/s,333comparing favorably with the true value of \py{f'{T:.2e}'} m²/s. Right panel displays fit residuals334(green circles) showing random scatter around zero within $\pm$2 cm (gray dashed lines), confirming335good agreement between measured and predicted drawdown. The early-time deviations reflect the336breakdown of the Cooper-Jacob approximation when $u > 0.01$.}337\end{figure}338339\section{Two-Dimensional Hydraulic Head Distribution}340341Numerical solution of the 2D groundwater flow equation enables visualization of hydraulic head342distributions and flow patterns under complex boundary conditions. Using finite difference343approximations on a discrete grid, we solve for steady-state head distributions in a confined344aquifer with a pumping well and specified constant-head boundaries. The resulting head contours345and flow vectors reveal the convergent radial flow pattern characteristic of well hydraulics.346347\begin{pycode}348# Solve 2D steady-state groundwater flow equation using finite differences349# Domain: 1000 m x 1000 m confined aquifer with central pumping well350351nx, ny = 101, 101 # Grid resolution352Lx, Ly = 1000, 1000 # Domain size (m)353dx = Lx / (nx - 1)354dy = Ly / (ny - 1)355356x = np.linspace(0, Lx, nx)357y = np.linspace(0, Ly, ny)358X, Y = np.meshgrid(x, y)359360# Initial head (m above datum)361h0 = 100.0362h = np.ones((ny, nx)) * h0363364# Boundary conditions: constant head on all boundaries365h[0, :] = h0 # bottom366h[-1, :] = h0 # top367h[:, 0] = h0 # left368h[:, -1] = h0 # right369370# Well location at center371i_well, j_well = ny // 2, nx // 2372r_well = 0.15 # well radius (m)373374# Effective well cell area375A_well = np.pi * r_well**2376# Pumping rate creates sink term377well_flux = Q / (dx * dy) # m/s378379# Iterative solver: Gauss-Seidel with successive over-relaxation (SOR)380omega = 1.5 # relaxation parameter381max_iter = 5000382tolerance = 1e-6383384for iteration in range(max_iter):385h_old = h.copy()386387for i in range(1, ny - 1):388for j in range(1, nx - 1):389# Standard finite difference stencil390h_new = 0.25 * (h[i+1, j] + h[i-1, j] + h[i, j+1] + h[i, j-1])391392# Apply well sink if at well location393if i == i_well and j == j_well:394# Modify equation for pumping well395source_term = -well_flux * dx * dy / T396h_new = 0.25 * (h[i+1, j] + h[i-1, j] + h[i, j+1] + h[i, j-1] - source_term)397398# Apply SOR399h[i, j] = omega * h_new + (1 - omega) * h[i, j]400401# Check convergence402max_change = np.max(np.abs(h - h_old))403if max_change < tolerance:404break405406# Calculate drawdown407drawdown = h0 - h408409# Calculate hydraulic gradients and specific discharge410dh_dx = np.zeros_like(h)411dh_dy = np.zeros_like(h)412dh_dx[:, 1:-1] = (h[:, 2:] - h[:, :-2]) / (2 * dx)413dh_dy[1:-1, :] = (h[2:, :] - h[:-2, :]) / (2 * dy)414415# Darcy velocity (specific discharge)416qx = -K * dh_dx417qy = -K * dh_dy418q_magnitude = np.sqrt(qx**2 + qy**2)419420# Create visualization421fig = plt.figure(figsize=(14, 12))422423# Plot 1: Hydraulic head contours424ax1 = plt.subplot(2, 2, 1)425levels_head = np.linspace(h.min(), h0, 25)426cs1 = ax1.contourf(X, Y, h, levels=levels_head, cmap='Blues_r')427cs1_lines = ax1.contour(X, Y, h, levels=15, colors='black', linewidths=0.8, alpha=0.4)428ax1.clabel(cs1_lines, inline=True, fontsize=7, fmt='%0.1f')429cbar1 = plt.colorbar(cs1, ax=ax1, label='Hydraulic head (m)')430ax1.plot(x[j_well], y[i_well], 'r*', markersize=20, markeredgecolor='black', markeredgewidth=1.5,431label='Pumping well')432ax1.set_xlabel('x (m)', fontsize=11)433ax1.set_ylabel('y (m)', fontsize=11)434ax1.set_title('Steady-State Hydraulic Head', fontsize=12, fontweight='bold')435ax1.legend(loc='upper right', fontsize=9)436ax1.set_aspect('equal')437438# Plot 2: Drawdown contours439ax2 = plt.subplot(2, 2, 2)440levels_dd = np.linspace(0, drawdown.max(), 25)441cs2 = ax2.contourf(X, Y, drawdown, levels=levels_dd, cmap='Reds')442cs2_lines = ax2.contour(X, Y, drawdown, levels=12, colors='black', linewidths=0.8, alpha=0.4)443ax2.clabel(cs2_lines, inline=True, fontsize=7, fmt='%0.2f')444cbar2 = plt.colorbar(cs2, ax=ax2, label='Drawdown (m)')445ax2.plot(x[j_well], y[i_well], 'k*', markersize=20, markeredgecolor='white', markeredgewidth=1.5)446ax2.set_xlabel('x (m)', fontsize=11)447ax2.set_ylabel('y (m)', fontsize=11)448ax2.set_title('Drawdown Cone of Depression', fontsize=12, fontweight='bold')449ax2.set_aspect('equal')450451# Plot 3: Flow field (quiver plot)452ax3 = plt.subplot(2, 2, 3)453skip = 5454ax3.contourf(X, Y, drawdown, levels=levels_dd, cmap='Reds', alpha=0.3)455Q_plot = ax3.quiver(X[::skip, ::skip], Y[::skip, ::skip],456qx[::skip, ::skip], qy[::skip, ::skip],457q_magnitude[::skip, ::skip], cmap='viridis', scale=5e-6)458cbar3 = plt.colorbar(Q_plot, ax=ax3, label='Specific discharge (m/s)')459ax3.plot(x[j_well], y[i_well], 'r*', markersize=20, markeredgecolor='black', markeredgewidth=1.5)460ax3.set_xlabel('x (m)', fontsize=11)461ax3.set_ylabel('y (m)', fontsize=11)462ax3.set_title('Groundwater Flow Field', fontsize=12, fontweight='bold')463ax3.set_aspect('equal')464465# Plot 4: Radial profile comparison466ax4 = plt.subplot(2, 2, 4)467# Extract drawdown along centerline468centerline_dd = drawdown[i_well, j_well:]469r_centerline = x[j_well:] - x[j_well]470# Analytical steady-state solution (Thiem equation for confined aquifer)471r_analytical = np.linspace(r_well, 500, 200)472# Thiem: s = Q/(2*pi*T) * ln(R/r) where R is radius of influence473R_influence = 500 # m474s_analytical = Q / (2 * np.pi * T) * np.log(R_influence / r_analytical)475476ax4.plot(r_centerline[1:], centerline_dd[1:], 'b-', linewidth=2.5, label='Numerical solution')477ax4.plot(r_analytical, s_analytical, 'r--', linewidth=2, label='Thiem equation')478ax4.set_xlabel('Radial distance from well (m)', fontsize=11)479ax4.set_ylabel('Drawdown (m)', fontsize=11)480ax4.set_title('Radial Drawdown Profile', fontsize=12, fontweight='bold')481ax4.legend(loc='upper right', fontsize=9)482ax4.grid(True, alpha=0.3)483ax4.set_xlim(0, 500)484485plt.tight_layout()486plt.savefig('groundwater_flow_2d_field.pdf', dpi=150, bbox_inches='tight')487plt.close()488489# Store key results490max_drawdown = drawdown.max()491drawdown_at_100m = drawdown[i_well, j_well + int(100/dx)]492\end{pycode}493494\begin{figure}[H]495\centering496\includegraphics[width=\textwidth]{groundwater_flow_2d_field.pdf}497\caption{Two-dimensional numerical solution of steady-state groundwater flow in 1000 m × 1000 m498confined aquifer with central pumping well (red star). Top-left panel shows hydraulic head499distribution with contour lines indicating head decline from 100 m at boundaries to500\py{f'{h[i_well, j_well]:.2f}'} m at the well. Top-right panel displays the cone of depression501with maximum drawdown of \py{f'{max_drawdown:.2f}'} m at the well location. Bottom-left panel502presents the convergent flow field as velocity vectors colored by specific discharge magnitude,503clearly showing radial flow toward the pumping well. Bottom-right panel compares the numerical504solution (blue line) with the analytical Thiem equation (red dashed), demonstrating excellent505agreement beyond the near-well region where numerical discretization effects become negligible.506At 100 m distance, drawdown is \py{f'{drawdown_at_100m:.3f}'} m.}507\end{figure}508509\section{Pumping Test Parameter Sensitivity}510511Understanding the sensitivity of drawdown to hydraulic parameters is crucial for interpreting512pumping test results and quantifying uncertainty in parameter estimates. We examine how variations513in transmissivity and storativity affect the time-drawdown response, revealing which parameters514dominate at different time scales. Early-time drawdown is primarily controlled by storativity,515while late-time response depends mainly on transmissivity.516517\begin{pycode}518# Sensitivity analysis: vary T and S independently519r_sensitivity = 50 # observation well distance520521# Transmissivity variation (S constant)522T_range = np.array([0.5, 0.75, 1.0, 1.5, 2.0]) * T523colors_T = plt.cm.Reds(np.linspace(0.4, 0.9, len(T_range)))524525# Storativity variation (T constant)526S_range = np.array([0.5, 0.75, 1.0, 1.5, 2.0]) * S527colors_S = plt.cm.Blues(np.linspace(0.4, 0.9, len(S_range)))528529fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13, 5))530531# Panel 1: Transmissivity sensitivity532for i, T_var in enumerate(T_range):533s_T = theis_drawdown(r_sensitivity, time_pumping, Q, T_var, S)534label_T = f'$T = {T_var/T:.2f} T_0$'535ax1.semilogx(time_pumping / 3600, s_T, linewidth=2.5, color=colors_T[i], label=label_T)536ax1.set_xlabel('Time (hours)', fontsize=11)537ax1.set_ylabel('Drawdown (m)', fontsize=11)538ax1.set_title('Transmissivity Sensitivity ($S$ constant)', fontsize=12, fontweight='bold')539ax1.legend(loc='lower right', fontsize=9)540ax1.grid(True, alpha=0.3, which='both')541542# Panel 2: Storativity sensitivity543for i, S_var in enumerate(S_range):544s_S = theis_drawdown(r_sensitivity, time_pumping, Q, T, S_var)545label_S = f'$S = {S_var/S:.2f} S_0$'546ax2.semilogx(time_pumping / 3600, s_S, linewidth=2.5, color=colors_S[i], label=label_S)547ax2.set_xlabel('Time (hours)', fontsize=11)548ax2.set_ylabel('Drawdown (m)', fontsize=11)549ax2.set_title('Storativity Sensitivity ($T$ constant)', fontsize=12, fontweight='bold')550ax2.legend(loc='lower right', fontsize=9)551ax2.grid(True, alpha=0.3, which='both')552553plt.tight_layout()554plt.savefig('groundwater_flow_sensitivity.pdf', dpi=150, bbox_inches='tight')555plt.close()556\end{pycode}557558\begin{figure}[H]559\centering560\includegraphics[width=\textwidth]{groundwater_flow_sensitivity.pdf}561\caption{Sensitivity of time-drawdown response to hydraulic parameters at observation well 50 m562from pumping well. Left panel shows the effect of varying transmissivity from 0.5$T_0$ to 2.0$T_0$563while holding storativity constant, demonstrating that higher transmissivity reduces drawdown564at all times by increasing the aquifer's ability to transmit water. Right panel illustrates565storativity variation from 0.5$S_0$ to 2.0$S_0$ with constant transmissivity, showing that566storativity primarily affects early-time response and the timing of drawdown propagation. Higher567storativity delays the arrival of the pressure transient and reduces early drawdown by providing568more water from storage. At late times ($t > 10$ hours), all storativity curves converge to569parallel straight lines with identical slopes, confirming that late-time Cooper-Jacob analysis570depends only on transmissivity.}571\end{figure}572573\section{Specific Capacity and Well Performance}574575Well performance is commonly characterized by specific capacity, defined as the pumping rate576divided by drawdown at the well. This metric provides a practical measure of well productivity577that depends on both aquifer properties (transmissivity) and well construction (radius, screen578efficiency). Understanding the relationship between specific capacity, pumping rate, and aquifer579parameters enables optimal wellfield design and prediction of long-term well performance.580581\begin{pycode}582# Well performance analysis583Q_range = np.linspace(0.005, 0.03, 10) # 5 to 30 L/s584r_w = 0.15 # well radius585t_performance = 24 * 3600 # drawdown after 24 hours586587# Calculate drawdown at well for different pumping rates588s_well = np.zeros(len(Q_range))589for i, Q_i in enumerate(Q_range):590s_well[i] = theis_drawdown(r_w, t_performance, Q_i, T, S)591592# Specific capacity593specific_capacity = Q_range / s_well # m^3/s per m = m^2/s594595# Well efficiency (actual vs theoretical)596# Theoretical drawdown (no well losses)597# Actual includes well losses: s_actual = s_formation + B*Q + C*Q^2598well_loss_coeff = 50 # s^2/m^5599s_well_loss = well_loss_coeff * Q_range**2600s_actual = s_well + s_well_loss601efficiency = s_well / s_actual * 100602603fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(13, 10))604605# Plot 1: Drawdown vs pumping rate606ax1.plot(Q_range * 1000, s_well, 'b-', linewidth=2.5, label='Formation loss')607ax1.plot(Q_range * 1000, s_actual, 'r--', linewidth=2.5, label='Total (with well loss)')608ax1.fill_between(Q_range * 1000, s_well, s_actual, alpha=0.3, color='orange', label='Well loss')609ax1.set_xlabel('Pumping rate (L/s)', fontsize=11)610ax1.set_ylabel('Drawdown at well (m)', fontsize=11)611ax1.set_title('Well Drawdown vs Pumping Rate', fontsize=12, fontweight='bold')612ax1.legend(loc='upper left', fontsize=9)613ax1.grid(True, alpha=0.3)614615# Plot 2: Specific capacity616ax2.plot(Q_range * 1000, specific_capacity * 1000, 'g-', linewidth=2.5, marker='o', markersize=8)617ax2.set_xlabel('Pumping rate (L/s)', fontsize=11)618ax2.set_ylabel('Specific capacity (L/s per m)', fontsize=11)619ax2.set_title('Specific Capacity Decline', fontsize=12, fontweight='bold')620ax2.grid(True, alpha=0.3)621622# Plot 3: Well efficiency623ax3.plot(Q_range * 1000, efficiency, 'm-', linewidth=2.5, marker='s', markersize=8)624ax3.axhline(y=70, color='red', linestyle='--', linewidth=2, alpha=0.7, label='70\\% threshold')625ax3.set_xlabel('Pumping rate (L/s)', fontsize=11)626ax3.set_ylabel('Well efficiency (\\%)', fontsize=11)627ax3.set_title('Well Efficiency vs Pumping Rate', fontsize=12, fontweight='bold')628ax3.legend(loc='upper right', fontsize=9)629ax3.grid(True, alpha=0.3)630ax3.set_ylim(50, 100)631632# Plot 4: Step-drawdown test simulation633Q_steps = np.array([0.01, 0.015, 0.02, 0.025]) * 1000 # L/s634step_duration = 3 * 3600 # 3 hours per step635t_step = np.arange(0, len(Q_steps) * step_duration, 60) # minute intervals636s_step = np.zeros_like(t_step, dtype=float)637638for i, (Q_step, t_start) in enumerate(zip(Q_steps, np.arange(0, len(Q_steps) * step_duration, step_duration))):639idx_step = (t_step >= t_start) & (t_step < t_start + step_duration)640t_since_start = t_step[idx_step] - t_start + 60 # avoid t=0641s_step[idx_step] = theis_drawdown(r_w, t_since_start, Q_step / 1000, T, S)642# Add cumulative effect from previous steps643for j in range(i):644t_since_prev = t_step[idx_step] - j * step_duration + 60645s_step[idx_step] += theis_drawdown(r_w, t_since_prev, Q_steps[j] / 1000, T, S)646647ax4.plot(t_step / 3600, s_step, 'b-', linewidth=2.5)648for i, t_start in enumerate(np.arange(0, len(Q_steps) * step_duration, step_duration)):649ax4.axvline(x=t_start / 3600, color='red', linestyle='--', alpha=0.7)650if i < len(Q_steps):651ax4.text(t_start / 3600 + 0.2, ax4.get_ylim()[1] * 0.9, f'$Q={Q_steps[i]:.0f}$ L/s',652fontsize=9, color='red', fontweight='bold')653ax4.set_xlabel('Time (hours)', fontsize=11)654ax4.set_ylabel('Drawdown at well (m)', fontsize=11)655ax4.set_title('Step-Drawdown Test Simulation', fontsize=12, fontweight='bold')656ax4.grid(True, alpha=0.3)657658plt.tight_layout()659plt.savefig('groundwater_flow_well_performance.pdf', dpi=150, bbox_inches='tight')660plt.close()661662# Calculate optimal pumping rate (max efficiency > 70%)663optimal_Q_idx = np.where(efficiency > 70)[0]664if len(optimal_Q_idx) > 0:665optimal_Q = Q_range[optimal_Q_idx[-1]] * 1000 # L/s666else:667optimal_Q = Q_range[0] * 1000668\end{pycode}669670\begin{figure}[H]671\centering672\includegraphics[width=\textwidth]{groundwater_flow_well_performance.pdf}673\caption{Well performance characteristics for aquifer with $T = \py{f'{T:.2e}'}$ m²/s and674$S = \py{f'{S:.2e}'}$. Top-left panel shows drawdown components: formation loss (blue) increases675linearly with pumping rate following the Theis equation, while well losses (orange shading)676contribute additional quadratic head loss from turbulent flow in the well screen and gravel pack.677Top-right panel displays specific capacity decline from \py{f'{specific_capacity[0]*1000:.1f}'} to678\py{f'{specific_capacity[-1]*1000:.1f}'} L/s/m as pumping rate increases, reflecting non-linear679well losses. Bottom-left panel shows well efficiency decreasing from \py{f'{efficiency[0]:.1f}'}\\%680to \py{f'{efficiency[-1]:.1f}'}\\% with the 70\\% threshold indicated by red dashed line,681suggesting optimal pumping rate near \py{f'{optimal_Q:.1f}'} L/s. Bottom-right panel presents682a step-drawdown test simulation with four pumping steps (10, 15, 20, 25 L/s), each lasting 3 hours,683used to determine formation loss and well loss coefficients.}684\end{figure}685686\section{Results Summary}687688\begin{pycode}689print(r"\begin{table}[H]")690print(r"\centering")691print(r"\caption{Aquifer Hydraulic Properties and Well Characteristics}")692print(r"\begin{tabular}{lcc}")693print(r"\toprule")694print(r"Parameter & Symbol & Value \\")695print(r"\midrule")696print(f"Hydraulic conductivity & $K$ & {K:.2e} m/s \\\\")697print(f"Aquifer thickness & $b$ & {b:.1f} m \\\\")698print(f"Transmissivity & $T$ & {T:.2e} m$^2$/s \\\\")699print(f"Storativity & $S$ & {S:.2e} (dimensionless) \\\\")700print(f"Pumping rate & $Q$ & {Q*1000:.1f} L/s \\\\")701print(f"Well radius & $r_w$ & {r_w:.2f} m \\\\")702print(r"\midrule")703print(f"Cooper-Jacob slope & $\\Delta s$/log cycle & {slope_cj:.3f} m \\\\")704print(f"Estimated transmissivity & $T_{est}$ & {T_estimated:.2e} m$^2$/s \\\\")705print(f"Estimated storativity & $S_{est}$ & {S_estimated:.2e} \\\\")706print(f"Parameter error (T) & $\\epsilon_T$ & {abs(T_estimated-T)/T*100:.1f}\\% \\\\")707print(f"Parameter error (S) & $\\epsilon_S$ & {abs(S_estimated-S)/S*100:.1f}\\% \\\\")708print(r"\midrule")709print(f"Max drawdown (24 hr) & $s_{max}$ & {max_drawdown:.2f} m \\\\")710print(f"Drawdown at 100 m & $s(r=100)$ & {drawdown_at_100m:.3f} m \\\\")711print(f"Optimal pumping rate & $Q_{opt}$ & {optimal_Q:.1f} L/s \\\\")712print(f"Specific capacity (15 L/s) & SC & {specific_capacity[5]*1000:.2f} L/s/m \\\\")713print(r"\bottomrule")714print(r"\end{tabular}")715print(r"\label{tab:results}")716print(r"\end{table}")717\end{pycode}718719\section{Discussion}720721\begin{example}[Field Application of Theis Analysis]722Consider a municipal water supply well pumping from a confined sandstone aquifer. Step-drawdown723testing reveals specific capacity of 8.5 L/s/m at the design pumping rate of 15 L/s. Using724the Theis equation with parameters determined from a 24-hour constant-rate test, engineers can:725\begin{itemize}726\item Predict long-term drawdown to ensure adequate saturated thickness727\item Design optimal well spacing in a wellfield to minimize interference728\item Estimate sustainable yield based on recharge and boundary conditions729\item Assess potential for saltwater intrusion in coastal aquifers730\end{itemize}731\end{example}732733\begin{remark}[Assumptions and Limitations]734The Theis solution assumes: (1) homogeneous, isotropic aquifer; (2) horizontal flow;735(3) instantaneous release from storage; (4) fully penetrating well; (5) infinite areal extent.736Real aquifers often violate these assumptions, requiring modifications such as:737\begin{itemize}738\item \textbf{Hantush-Jacob} solution for leaky confined aquifers \cite{hantush1955non}739\item \textbf{Neuman} solution for unconfined aquifers with delayed yield \cite{neuman1972theory}740\item \textbf{Boulton} formulation for dual-porosity systems \cite{boulton1954unsteady}741\item \textbf{Anisotropic} formulations with directional hydraulic conductivity742\end{itemize}743\end{remark}744745\begin{remark}[Numerical Methods]746The finite difference solution presented here uses iterative relaxation (SOR method) for747steady-state problems. For transient simulations, explicit or implicit time-stepping schemes748solve the time-dependent equation. Implicit methods (Crank-Nicolson, backward Euler) offer749unconditional stability but require solving linear systems at each timestep. Modern groundwater750codes like MODFLOW implement sophisticated preconditioned conjugate gradient solvers for751large 3D domains \cite{wang2000introduction}.752\end{remark}753754\subsection{Comparison with Analytical Solutions}755756The numerical steady-state solution shows excellent agreement with the Thiem equation beyond757a few grid cells from the well. Near the well, discretization error increases because the758finite grid cannot properly resolve the logarithmic singularity at $r = r_w$. Adaptive mesh759refinement or analytical element methods can improve near-well accuracy.760761\section{Conclusions}762763This analysis demonstrates comprehensive application of groundwater flow theory to practical764aquifer characterization and well hydraulics:765766\begin{enumerate}767\item Pumping test analysis using the Cooper-Jacob method yields transmissivity768$T = \py{f"{T_estimated:.2e}"}$ m²/s and storativity $S = \py{f"{S_estimated:.2e}"}$,769with parameter errors of \py{f"{abs(T_estimated-T)/T*100:.1f}"}\\% and770\py{f"{abs(S_estimated-S)/S*100:.1f}"}\\% respectively771772\item Two-dimensional finite difference solution reveals the radial flow field converging773toward the pumping well with maximum drawdown of \py{f"{max_drawdown:.2f}"} m at steady state774775\item Sensitivity analysis confirms that transmissivity controls late-time drawdown and776Cooper-Jacob slope, while storativity primarily affects early-time response and timing777778\item Well performance analysis indicates optimal pumping rate near \py{f"{optimal_Q:.1f}"} L/s779to maintain well efficiency above 70\\%, balancing production against well losses780781\item Numerical and analytical solutions agree within discretization error, validating782both approaches for engineering applications783\end{enumerate}784785The methods presented form the foundation for groundwater resource assessment, sustainable786yield determination, and wellfield optimization in water supply and remediation applications.787788\bibliographystyle{unsrt}789\bibliography{groundwater_flow}790791\end{document}792793794