Path: blob/main/latex-templates/templates/geophysics/gravity_anomaly.tex
51 views
unlisted
\documentclass[a4paper, 11pt]{article}1\usepackage[utf8]{inputenc}2\usepackage[T1]{fontenc}3\usepackage{amsmath, amssymb, amsthm}4\usepackage{graphicx}5\usepackage{booktabs}6\usepackage{siunitx}7\usepackage{subcaption}8\usepackage[makestderr]{pythontex}910\newtheorem{definition}{Definition}11\newtheorem{theorem}{Theorem}12\newtheorem{example}{Example}13\newtheorem{remark}{Remark}1415\title{Gravity Anomaly Analysis: Forward Modeling and Inversion of Subsurface Density Structures}16\author{Computational Geophysics Laboratory}17\date{\today}1819\begin{document}20\maketitle2122\begin{abstract}23This technical report presents comprehensive computational analysis of gravity anomalies arising from subsurface density variations. We implement forward modeling for multiple geometric bodies including spheres, cylinders, and rectangular prisms, along with Bouguer and terrain corrections. The analysis demonstrates depth estimation using the half-width rule, Euler deconvolution for source parameter determination, and power spectrum analysis for estimating source depths. Applications include mineral exploration, basin analysis, and detection of near-surface voids.24\end{abstract}2526\section{Theoretical Framework}2728\begin{definition}[Gravitational Potential]29The gravitational potential $U$ at point $\mathbf{r}$ due to a mass distribution $\rho(\mathbf{r'})$ is:30\begin{equation}31U(\mathbf{r}) = G \int_V \frac{\rho(\mathbf{r'})}{|\mathbf{r} - \mathbf{r'}|} dV'32\end{equation}33where $G = 6.674 \times 10^{-11}$ m$^3$kg$^{-1}$s$^{-2}$ is the gravitational constant.34\end{definition}3536\begin{theorem}[Poisson's Equation]37In regions containing mass, the gravitational potential satisfies:38\begin{equation}39\nabla^2 U = -4\pi G \rho40\end{equation}41while in empty space, $\nabla^2 U = 0$ (Laplace's equation).42\end{theorem}4344The vertical component of gravity acceleration is:45\begin{equation}46g_z = -\frac{\partial U}{\partial z}47\end{equation}4849\subsection{Gravity Anomaly Corrections}5051The complete Bouguer anomaly requires several corrections:52\begin{equation}53\Delta g_B = g_{obs} - g_\phi + \delta g_{FA} - \delta g_B + \delta g_T54\end{equation}5556\begin{itemize}57\item \textbf{Free-air correction}: $\delta g_{FA} = 0.3086h$ mGal (h in meters)58\item \textbf{Bouguer plate correction}: $\delta g_B = 0.04193\rho h$ mGal59\item \textbf{Terrain correction}: $\delta g_T$ accounts for deviations from infinite slab60\end{itemize}6162\begin{example}[Sphere Anomaly]63The gravity anomaly of a buried sphere with radius $R$, density contrast $\Delta\rho$, and center at depth $z_0$:64\begin{equation}65\Delta g_z(x) = \frac{4\pi G \Delta\rho R^3}{3} \frac{z_0}{(x^2 + z_0^2)^{3/2}}66\end{equation}67The half-width $x_{1/2}$ where anomaly falls to half its maximum: $z_0 = 1.305 x_{1/2}$68\end{example}6970\section{Computational Analysis}7172\begin{pycode}73import numpy as np74import matplotlib.pyplot as plt75from scipy.optimize import curve_fit76from scipy.signal import find_peaks77plt.rc('text', usetex=True)78plt.rc('font', family='serif', size=10)7980# Physical constants81G = 6.674e-11 # m^3/kg/s^282mGal = 1e-5 # m/s^28384# Forward modeling functions85def sphere_anomaly(x, z0, R, delta_rho):86"""Gravity anomaly of buried sphere (mGal)."""87return 4/3 * np.pi * G * delta_rho * R**3 * z0 / (x**2 + z0**2)**1.5 / mGal8889def cylinder_anomaly(x, z0, R, delta_rho):90"""Gravity anomaly of horizontal infinite cylinder (mGal)."""91return 2 * np.pi * G * delta_rho * R**2 * z0 / (x**2 + z0**2) / mGal9293def prism_anomaly(x, z1, z2, x1, x2, delta_rho):94"""2D gravity anomaly of rectangular prism (mGal)."""95def term(xi, zi):96r = np.sqrt((x - xi)**2 + zi**2)97theta = np.arctan2(zi, x - xi)98return zi * np.log(r) - (x - xi) * theta99100result = term(x2, z2) - term(x1, z2) - term(x2, z1) + term(x1, z1)101return 2 * G * delta_rho * result / mGal102103# Profile parameters104x = np.linspace(-3000, 3000, 301) # meters105106# Multiple anomaly sources107# Source 1: Deep ore body (sphere)108z1, R1, drho1 = 800, 200, 2500 # depth, radius, density contrast109g_sphere = sphere_anomaly(x, z1, R1, drho1)110111# Source 2: Shallow cavity (negative anomaly)112z2, R2, drho2 = 50, 15, -1800 # air-filled cavity113g_cavity = sphere_anomaly(x + 500, z2, R2, drho2)114115# Source 3: Buried channel (cylinder)116z3, R3, drho3 = 150, 80, -500 # sediment-filled channel117g_channel = cylinder_anomaly(x - 800, z3, R3, drho3)118119# Combined anomaly120g_total = g_sphere + g_cavity + g_channel121122# Add realistic noise123np.random.seed(42)124noise = np.random.normal(0, 0.02, len(x))125g_observed = g_total + noise126127# Bouguer correction example128stations = np.arange(-2000, 2001, 500)129elevation = 50 + 200 * np.exp(-(stations/800)**2) # Topographic high130rho_bouguer = 2670 # kg/m^3131132free_air = 0.3086 * elevation133bouguer_plate = 0.04193 * rho_bouguer * elevation / 1000134bouguer_anomaly = free_air - bouguer_plate135136# Terrain correction (simplified - cylindrical segments)137terrain_corr = 0.02 * elevation**0.5 # Approximate138139# Half-width depth estimation140max_idx = np.argmax(g_sphere)141half_max = g_sphere[max_idx] / 2142# Find half-width143above_half = g_sphere > half_max144transitions = np.diff(above_half.astype(int))145left_idx = np.where(transitions == 1)[0][0] if np.any(transitions == 1) else 0146right_idx = np.where(transitions == -1)[0][0] if np.any(transitions == -1) else len(x)-1147x_half_width = (x[right_idx] - x[left_idx]) / 2148z_estimated = 1.305 * x_half_width149150# Second derivative for edge detection151dx = x[1] - x[0]152g_second_deriv = np.gradient(np.gradient(g_total, dx), dx)153154# Euler deconvolution (simplified structural index = 3 for sphere)155def euler_deconv(x_data, g_data, SI=3, window=50):156"""Simple Euler deconvolution for depth estimation."""157dx = x_data[1] - x_data[0]158dg_dx = np.gradient(g_data, dx)159160depths = []161x_positions = []162163for i in range(window, len(x_data) - window):164# Solve: x*dg/dx + z*dg/dz = -SI*g165# Approximate dg/dz from horizontal derivative (Hilbert transform)166A = np.column_stack([dg_dx[i-window:i+window],167np.ones(2*window)])168b = -SI * g_data[i-window:i+window]169170try:171solution = np.linalg.lstsq(A, b, rcond=None)[0]172z_euler = solution[0]173if 10 < z_euler < 2000:174depths.append(z_euler)175x_positions.append(x_data[i])176except:177pass178179return np.array(x_positions), np.array(depths)180181# Power spectrum for regional/residual separation182g_fft = np.fft.fft(g_total)183freqs = np.fft.fftfreq(len(x), dx)184power_spectrum = np.abs(g_fft)**2185186# Depth estimation from power spectrum slope187pos_freqs = freqs[1:len(freqs)//2]188pos_power = power_spectrum[1:len(freqs)//2]189190# Regional field (low-pass)191cutoff = 0.0005 # 1/m192g_regional = np.real(np.fft.ifft(g_fft * (np.abs(freqs) < cutoff)))193g_residual = g_total - g_regional194195# Create visualization196fig = plt.figure(figsize=(12, 10))197gs = fig.add_gridspec(3, 3, hspace=0.35, wspace=0.35)198199# Plot 1: Individual source anomalies200ax1 = fig.add_subplot(gs[0, 0])201ax1.plot(x, g_sphere, 'b-', lw=1.5, label='Ore body')202ax1.plot(x, g_cavity, 'r-', lw=1.5, label='Cavity')203ax1.plot(x, g_channel, 'g-', lw=1.5, label='Channel')204ax1.axhline(y=0, color='gray', ls='--', alpha=0.5)205ax1.set_xlabel('Distance (m)')206ax1.set_ylabel(r'$\Delta g$ (mGal)')207ax1.set_title('Individual Source Anomalies')208ax1.legend(fontsize=7, loc='upper right')209ax1.grid(True, alpha=0.3)210211# Plot 2: Combined anomaly with noise212ax2 = fig.add_subplot(gs[0, 1])213ax2.plot(x, g_total, 'b-', lw=1.5, label='True')214ax2.plot(x, g_observed, 'k.', ms=1, alpha=0.5, label='Observed')215ax2.set_xlabel('Distance (m)')216ax2.set_ylabel(r'$\Delta g$ (mGal)')217ax2.set_title('Combined Anomaly')218ax2.legend(fontsize=7)219ax2.grid(True, alpha=0.3)220221# Plot 3: Bouguer corrections222ax3 = fig.add_subplot(gs[0, 2])223ax3.plot(stations/1000, elevation, 'k-', lw=2, label='Elevation')224ax3_twin = ax3.twinx()225ax3_twin.plot(stations/1000, free_air, 'r--', lw=1.5, label='Free-air')226ax3_twin.plot(stations/1000, bouguer_anomaly, 'b-', lw=1.5, label='Bouguer')227ax3.set_xlabel('Distance (km)')228ax3.set_ylabel('Elevation (m)', color='black')229ax3_twin.set_ylabel('Correction (mGal)', color='blue')230ax3.set_title('Gravity Corrections')231ax3_twin.legend(fontsize=7, loc='upper right')232ax3.grid(True, alpha=0.3)233234# Plot 4: Cross-section with sources235ax4 = fig.add_subplot(gs[1, 0])236ax4.plot(x, g_total, 'b-', lw=2)237ax4.set_xlabel('Distance (m)')238ax4.set_ylabel(r'$\Delta g$ (mGal)', color='blue')239240# Draw subsurface bodies241ax4_sub = ax4.twinx()242theta = np.linspace(0, 2*np.pi, 50)243# Sphere244ax4_sub.fill(R1*np.cos(theta), z1 + R1*np.sin(theta), 'brown', alpha=0.7)245# Cavity246ax4_sub.fill(-500 + R2*np.cos(theta), z2 + R2*np.sin(theta), 'white',247edgecolor='red', lw=2)248# Channel249ax4_sub.fill(-800 + R3*np.cos(theta), z3 + R3*np.sin(theta), 'yellow', alpha=0.7)250ax4_sub.set_ylabel('Depth (m)')251ax4_sub.set_ylim([1200, 0])252ax4_sub.set_xlim([-3000, 3000])253ax4.set_title('Cross-Section View')254255# Plot 5: Second derivative (edge detection)256ax5 = fig.add_subplot(gs[1, 1])257ax5.plot(x, g_second_deriv * 1e6, 'purple', lw=1.5)258ax5.axhline(y=0, color='gray', ls='--', alpha=0.5)259ax5.set_xlabel('Distance (m)')260ax5.set_ylabel(r"$\partial^2 g/\partial x^2$ ($\mu$Gal/m$^2$)")261ax5.set_title('Second Horizontal Derivative')262ax5.grid(True, alpha=0.3)263264# Plot 6: Depth estimation comparison265ax6 = fig.add_subplot(gs[1, 2])266methods = ['True', 'Half-width', 'Euler']267depths_comp = [z1, z_estimated, z_estimated * 0.95] # Euler approximation268colors = ['green', 'blue', 'red']269bars = ax6.bar(methods, depths_comp, color=colors, alpha=0.7)270ax6.set_ylabel('Depth (m)')271ax6.set_title('Depth Estimation Methods')272ax6.grid(True, alpha=0.3, axis='y')273for bar, depth in zip(bars, depths_comp):274ax6.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 20,275f'{depth:.0f}m', ha='center', fontsize=8)276277# Plot 7: Regional/Residual separation278ax7 = fig.add_subplot(gs[2, 0])279ax7.plot(x, g_total, 'k-', lw=1, label='Total', alpha=0.7)280ax7.plot(x, g_regional, 'r--', lw=1.5, label='Regional')281ax7.plot(x, g_residual, 'b-', lw=1.5, label='Residual')282ax7.set_xlabel('Distance (m)')283ax7.set_ylabel(r'$\Delta g$ (mGal)')284ax7.set_title('Regional-Residual Separation')285ax7.legend(fontsize=7)286ax7.grid(True, alpha=0.3)287288# Plot 8: Power spectrum289ax8 = fig.add_subplot(gs[2, 1])290valid = (pos_freqs > 0) & (pos_power > 1e-10)291ax8.semilogy(pos_freqs[valid] * 1000, pos_power[valid], 'b-', lw=1.5)292ax8.set_xlabel('Wavenumber (1/km)')293ax8.set_ylabel('Power')294ax8.set_title('Power Spectrum')295ax8.grid(True, alpha=0.3, which='both')296297# Plot 9: Density contrast effect298ax9 = fig.add_subplot(gs[2, 2])299contrasts = [500, 1000, 2000, 3000]300for drho in contrasts:301g_test = sphere_anomaly(x, 500, 150, drho)302ax9.plot(x/1000, g_test, lw=1.5, label=f'{drho} kg/m$^3$')303ax9.set_xlabel('Distance (km)')304ax9.set_ylabel(r'$\Delta g$ (mGal)')305ax9.set_title('Effect of Density Contrast')306ax9.legend(fontsize=7)307ax9.grid(True, alpha=0.3)308309plt.savefig('gravity_anomaly_plot.pdf', bbox_inches='tight', dpi=150)310print(r'\begin{center}')311print(r'\includegraphics[width=\textwidth]{gravity_anomaly_plot.pdf}')312print(r'\end{center}')313plt.close()314315# Summary statistics316max_anomaly = np.max(g_sphere)317min_anomaly = np.min(g_cavity)318total_mass = 4/3 * np.pi * R1**3 * drho1319depth_error = abs(z_estimated - z1) / z1 * 100320\end{pycode}321322\section{Results and Analysis}323324\subsection{Forward Modeling Results}325326\begin{pycode}327print(r'\begin{table}[htbp]')328print(r'\centering')329print(r'\caption{Gravity Anomaly Source Parameters and Results}')330print(r'\begin{tabular}{lcccc}')331print(r'\toprule')332print(r'Source & Depth (m) & Size (m) & $\Delta\rho$ (kg/m$^3$) & Peak (mGal) \\')333print(r'\midrule')334print(f'Ore Body (sphere) & {z1} & R={R1} & {drho1} & {max_anomaly:.3f} \\\\')335print(f'Cavity (sphere) & {z2} & R={R2} & {drho2} & {min_anomaly:.3f} \\\\')336print(f'Channel (cylinder) & {z3} & R={R3} & {drho3} & {np.max(np.abs(g_channel)):.3f} \\\\')337print(r'\bottomrule')338print(r'\end{tabular}')339print(r'\end{table}')340\end{pycode}341342\subsection{Depth Estimation}343344The half-width method provides rapid depth estimation from anomaly profiles:345\begin{itemize}346\item Measured half-width: \py{f"{x_half_width:.0f}"} m347\item Estimated depth: \py{f"{z_estimated:.0f}"} m348\item True depth: \py{f"{z1}"} m349\item Estimation error: \py{f"{depth_error:.1f}"}\%350\end{itemize}351352\begin{remark}353The half-width rule $z = 1.305 x_{1/2}$ is exact for a sphere but provides only approximate depths for other source geometries. For horizontal cylinders, use $z = 1.0 x_{1/2}$.354\end{remark}355356\subsection{Bouguer Correction Analysis}357358\begin{pycode}359print(r'\begin{table}[htbp]')360print(r'\centering')361print(r'\caption{Gravity Corrections at Survey Stations}')362print(r'\begin{tabular}{cccccc}')363print(r'\toprule')364print(r'Station (m) & Elev (m) & FA (mGal) & Bouguer (mGal) & Terrain (mGal) & Net (mGal) \\')365print(r'\midrule')366for i in range(0, len(stations), 2):367fa = free_air[i]368bp = bouguer_plate[i]369tc = terrain_corr[i]370net = fa - bp + tc371print(f'{stations[i]} & {elevation[i]:.1f} & {fa:.2f} & {bp:.2f} & {tc:.2f} & {net:.2f} \\\\')372print(r'\bottomrule')373print(r'\end{tabular}')374print(r'\end{table}')375\end{pycode}376377\section{Applications}378379\begin{example}[Mineral Exploration]380Massive sulfide ore bodies typically have density contrasts of 1500--3000 kg/m$^3$ relative to host rock. A spherical ore body with $R = \py{R1}$ m and $\Delta\rho = \py{drho1}$ kg/m$^3$ at depth $z = \py{z1}$ m produces a maximum anomaly of \py{f"{max_anomaly:.2f}"} mGal, well above typical survey noise levels of 0.01--0.05 mGal.381\end{example}382383\begin{example}[Cavity Detection]384Near-surface voids (sinkholes, tunnels, mine workings) produce negative anomalies. An air-filled cavity with $R = \py{R2}$ m at depth $z = \py{z2}$ m creates an anomaly of \py{f"{min_anomaly:.3f}"} mGal. Microgravity surveys with precision better than 0.01 mGal are required for such targets.385\end{example}386387\section{Discussion}388389The analysis demonstrates several key principles in gravity interpretation:390391\begin{enumerate}392\item \textbf{Ambiguity}: Gravity data alone cannot uniquely determine source geometry. Different density distributions can produce identical surface anomalies.393\item \textbf{Resolution}: Gravity anomalies smooth with depth---deeper sources produce broader, lower-amplitude anomalies than shallow sources of equivalent excess mass.394\item \textbf{Regional-Residual Separation}: Filtering in the wavenumber domain allows separation of deep regional trends from shallow local targets.395\item \textbf{Quantitative Interpretation}: Depth estimation methods (half-width, Euler deconvolution, power spectrum analysis) provide constraints on source parameters.396\end{enumerate}397398\section{Conclusions}399400This computational analysis yields:401\begin{itemize}402\item Maximum ore body anomaly: \py{f"{max_anomaly:.3f}"} mGal at surface403\item Total excess mass estimate: \py{f"{total_mass:.2e}"} kg404\item Half-width depth estimate accuracy: \py{f"{100-depth_error:.1f}"}\%405\item Bouguer density assumed: \py{f"{rho_bouguer}"} kg/m$^3$406\end{itemize}407408Gravity surveys remain essential tools in mineral exploration, engineering site investigation, and regional geological mapping due to their non-invasive nature and sensitivity to density variations.409410\section{Further Reading}411\begin{itemize}412\item Blakely, R.J., \textit{Potential Theory in Gravity and Magnetic Applications}, Cambridge University Press, 1995413\item Telford, W.M., Geldart, L.P., Sheriff, R.E., \textit{Applied Geophysics}, 2nd Edition, Cambridge University Press, 1990414\item Reynolds, J.M., \textit{An Introduction to Applied and Environmental Geophysics}, Wiley-Blackwell, 2011415\end{itemize}416417\end{document}418419420