
从零实现地震波模拟MATLAB二维声波方程有限差分实战指南地震波模拟是地球物理勘探、地震工程和计算物理领域的核心技术之一。通过数值模拟我们可以在计算机上重现地震波在地下介质中的传播过程为资源勘探、地质灾害评估等提供重要依据。本文将带你从零开始用MATLAB实现二维声波方程的有限差分模拟无需深厚数学背景只需基本编程知识即可上手。1. 环境准备与基础理论在开始编写代码前我们需要明确几个关键概念和准备工作。有限差分法Finite Difference Method是一种将微分方程转化为差分方程的数值方法通过离散化空间和时间来近似求解连续的偏微分方程。MATLAB环境配置要求MATLAB R2018a或更高版本至少8GB内存对于大型模型推荐使用独立显卡以加速可视化二维声波方程可以表示为1/v² * ∂²U/∂t² ∂²U/∂x² ∂²U/∂z²其中v 是波速m/sU 是波场变量位移或压力t 是时间x 和 z 是空间坐标有限差分近似公式使用二阶中心差分格式我们可以将偏导数近似为% 空间二阶导数 d2U_dx2 (U(i1,j) - 2*U(i,j) U(i-1,j)) / dx^2; d2U_dz2 (U(i,j1) - 2*U(i,j) U(i,j-1)) / dz^2; % 时间二阶导数 d2U_dt2 (U_next(i,j) - 2*U_now(i,j) U_prev(i,j)) / dt^2;2. 核心算法实现2.1 参数设置与网格初始化首先我们需要定义模拟的基本参数和计算网格% 模拟参数 Endtime 0.5; % 模拟时长(s) delta_t 0.0005; % 时间步长(s) delta_x 6; % x方向空间步长(m) delta_z 6; % z方向空间步长(m) CNX 301; % x方向网格数 CNZ 301; % z方向网格数 v 1500; % 波速(m/s) % 震源参数 Sx round(CNX/2); % 震源x坐标 Sz round(CNZ/2); % 震源z坐标 f0 30; % 主频(Hz) % 初始化波场 U_now zeros(CNX, CNZ); % 当前时刻波场 U_prev zeros(CNX, CNZ); % 前一时刻波场 U_next zeros(CNX, CNZ); % 下一时刻波场2.2 时间迭代循环核心的时间迭代过程如下for time 0:delta_t:Endtime % 空间循环更新波场 for i 2:CNX-1 for j 2:CNZ-1 % 计算空间二阶导数 A (U_now(i1,j) - 2*U_now(i,j) U_now(i-1,j)) / delta_x^2; B (U_now(i,j1) - 2*U_now(i,j) U_now(i,j-1)) / delta_z^2; % 更新下一时刻波场 U_next(i,j) 2*U_now(i,j) - U_prev(i,j) v^2 * (A B) * delta_t^2; end end % 添加震源函数高斯脉冲 U_next(Sx,Sz) 5.76*f0^2*(1-16*(0.6*f0*time-1)^2)*exp(-8*(0.6*f0*time-1)^2); % 更新波场 U_prev U_now; U_now U_next; end2.3 震源函数设计地震模拟中震源函数的选择直接影响模拟结果。我们使用高斯脉冲作为震源function source gaussian_source(f0, time) source 5.76*f0^2*(1-16*(0.6*f0*time-1)^2)*exp(-8*(0.6*f0*time-1)^2); end该函数在时域上表现为一个短时脉冲频域上则覆盖较宽的频率范围适合用于数值模拟。3. 边界条件处理3.1 边界问题的挑战在有限区域模拟中边界会反射波场导致虚假反射干扰模拟结果。常见的边界处理方法包括吸收边界条件ABC逐渐吸收到达边界的波场能量完美匹配层PML在边界外添加特殊层完全吸收入射波周期边界条件假设边界是周期性的3.2 简单吸收边界实现以下是一个简单的吸收边界实现示例% 边界吸收系数0-1之间1表示完全吸收 absorb_coef 0.3; % 在时间迭代循环中添加边界处理 for i 1:CNX % 上边界 U_next(i,1) U_next(i,2) * (1 - absorb_coef); % 下边界 U_next(i,CNZ) U_next(i,CNZ-1) * (1 - absorb_coef); end for j 1:CNZ % 左边界 U_next(1,j) U_next(2,j) * (1 - absorb_coef); % 右边界 U_next(CNX,j) U_next(CNX-1,j) * (1 - absorb_coef); end4. 结果可视化与分析4.1 波场快照可视化使用MATLAB的surf函数可以直观展示波场传播figure; surf(U_now); shading interp; view(2); % 俯视图 colormap(jet); % 使用jet色图 colorbar; % 显示色标 title(sprintf(Wavefield at t %.3f s, time)); xlabel(X Position (m)); ylabel(Z Position (m)); drawnow; % 实时更新图形4.2 波动动画制作要创建波动传播动画可以修改时间循环figure; for time 0:delta_t:Endtime % ...波场更新代码 % 绘制当前波场 surf(U_now); shading interp; view(2); colormap(jet); caxis([-1 1]); % 固定色标范围 title(sprintf(Wave Propagation at t %.3f s, time)); drawnow; % 捕获帧用于制作动画 frame getframe(gcf); writeVideo(video_writer, frame); end4.3 地震记录生成在地表设置接收器记录地震信号% 初始化地震记录 seismogram zeros(CNX, round(Endtime/delta_t)1); record_idx 1; for time 0:delta_t:Endtime % ...波场更新代码 % 记录地表j1的波场 seismogram(:, record_idx) U_now(:, 1); record_idx record_idx 1; end % 绘制地震记录 figure; imagesc(seismogram); xlabel(Time Step); ylabel(Receiver Position); title(Seismogram); colorbar;5. 性能优化技巧5.1 向量化计算MATLAB中应尽量避免使用循环改用矩阵运算% 向量化更新波场内部区域 i 2:CNX-1; j 2:CNZ-1; A (U_now(i1,j) - 2*U_now(i,j) U_now(i-1,j)) / delta_x^2; B (U_now(i,j1) - 2*U_now(i,j) U_now(i,j-1)) / delta_z^2; U_next(i,j) 2*U_now(i,j) - U_prev(i,j) v^2 * (A B) * delta_t^2;5.2 并行计算对于大型模型可以使用并行计算工具箱% 启用并行池 if isempty(gcp(nocreate)) parpool; end % 将空间循环改为parfor parfor i 2:CNX-1 for j 2:CNZ-1 % 波场更新代码 end end5.3 内存预分配确保所有数组在使用前已预分配内存避免动态增长% 预分配波场数组 U_now zeros(CNX, CNZ, single); % 使用单精度节省内存 U_prev zeros(CNX, CNZ, single); U_next zeros(CNX, CNZ, single);6. 常见问题与调试技巧6.1 数值不稳定性有限差分法需要满足CFL稳定性条件% 检查CFL条件 CFL v * delta_t * sqrt(1/delta_x^2 1/delta_z^2); if CFL 1 error(CFL条件不满足请减小时间步长或增大空间步长); end6.2 频散现象高频分量可能出现数值频散解决方案包括使用更高阶差分格式减小空间步长添加人工衰减6.3 震源位置选择震源应远离边界以避免过早的边界反射干扰% 确保震源不在边界附近 assert(Sx 10 Sx CNX-10 Sz 10 Sz CNZ-10, ... 震源位置太靠近边界);7. 进阶扩展7.1 非均匀介质模型通过定义速度场实现非均匀介质% 创建分层速度模型 v_model ones(CNX, CNZ) * 1500; % 默认速度 v_model(:, 151:end) 2000; % 下半层速度更高 % 在波场更新中使用速度场 U_next(i,j) 2*U_now(i,j) - U_prev(i,j) v_model(i,j)^2 * (A B) * delta_t^2;7.2 高阶差分格式四阶空间差分可减少数值频散% 四阶空间差分 A (-2.5*U_now(i,j) 4/3*(U_now(i1,j)U_now(i-1,j)) ... -1/12*(U_now(i2,j)U_now(i-2,j))) / delta_x^2;7.3 弹性波方程扩展将声波方程扩展到弹性波方程% 弹性波需要模拟应力和速度分量 rho 2000; % 密度(kg/m^3) lambda 3e9; % Lamé参数 mu 2e9; % Lamé参数 % 需要分别更新应力分量和速度分量8. 实际应用案例8.1 地下结构成像通过调整速度模型模拟不同地质结构% 创建含异常体的速度模型 v_model ones(CNX, CNZ) * 2000; v_model(100:120, 100:150) 2500; % 高速异常体 v_model(200:220, 150:200) 1800; % 低速异常体8.2 地震响应分析模拟不同震源特性对波场的影响% 不同主频的震源 f0_list [10, 20, 30, 40]; % Hz for f0 f0_list % 运行模拟并比较结果 end8.3 参数敏感性研究系统研究网格大小、时间步长的影响delta_x_list [4, 6, 8, 10]; % 不同空间步长 for dx delta_x_list dz dx; % 运行模拟并记录结果质量 end9. 代码优化与工程实践9.1 模块化设计将代码分解为可重用的函数% wave_simulation.m 主脚本 params set_parameters(); model create_velocity_model(params); [seismogram, snapshots] run_simulation(params, model); visualize_results(params, seismogram, snapshots); % 各功能模块放在单独文件中 function params set_parameters() % 参数设置函数 end9.2 单元测试为关键组件编写测试用例% 测试差分算子的准确性 U_test sin(2*pi*(0:0.1:1)); d2U finite_difference_2nd(U_test, 0.1); assert(max(abs(d2U(2:end-1) 4*pi^2*U_test(2:end-1))) 0.01);9.3 性能分析使用MATLAB Profiler识别性能瓶颈profile on; % 运行模拟代码 profile off; profile viewer;10. 资源与进一步学习10.1 推荐参考资料《计算地震学》- 张海明《Theory of Elastic Waves》- C. C. Mow《Finite-Difference Modeling of Earthquake Motions》- Peter Moczo10.2 开源项目参考SPECFEM - 专业级地震模拟软件OpenSWPC - 开源波动传播代码FDTD - 通用有限差分时域模拟框架10.3 在线课程Coursera Computational Methods for GeophysicistsMIT OpenCourseWare Wave PropagationSEG (勘探地球物理学家协会) 在线研讨会11. 完整代码示例以下是整合了上述所有要点的完整MATLAB实现function seismic_simulation_2D() % 参数设置 params struct(); params.Endtime 0.5; % 模拟时长(s) params.delta_t 0.0005; % 时间步长(s) params.delta_x 6; % x方向空间步长(m) params.delta_z 6; % z方向空间步长(m) params.CNX 301; % x方向网格数 params.CNZ 301; % z方向网格数 params.Sx round(params.CNX/2); % 震源x坐标 params.Sz round(params.CNZ/2); % 震源z坐标 params.f0 30; % 主频(Hz) % 创建速度模型 v_model create_velocity_model(params); % 运行模拟 [seismogram, snapshots] run_simulation(params, v_model); % 可视化结果 visualize_results(params, seismogram, snapshots); end function v_model create_velocity_model(params) % 创建分层速度模型 v_model ones(params.CNX, params.CNZ) * 1500; % 默认速度1500m/s v_model(:, 151:end) 2000; % 下半层速度2000m/s % 添加圆形异常体 [X, Z] meshgrid(1:params.CNX, 1:params.CNZ); circle (X-100).^2 (Z-100).^2 400; % 半径20网格点 v_model(circle) 2500; end function [seismogram, snapshots] run_simulation(params, v_model) % 初始化波场 U_now zeros(params.CNX, params.CNZ, single); U_prev U_now; U_next U_now; % 初始化地震记录 n_steps round(params.Endtime / params.delta_t) 1; seismogram zeros(params.CNX, n_steps); snapshots cell(1, floor(n_steps/50)); % 每50步保存一个快照 % 检查CFL条件 CFL max(v_model(:)) * params.delta_t * sqrt(1/params.delta_x^2 1/params.delta_z^2); if CFL 1 error(CFL条件不满足(%.2f)请减小时间步长或增大空间步长, CFL); end % 时间迭代 snapshot_idx 1; for step 1:n_steps time (step-1) * params.delta_t; % 向量化更新波场内部区域 i 2:params.CNX-1; j 2:params.CNZ-1; A (U_now(i1,j) - 2*U_now(i,j) U_now(i-1,j)) / params.delta_x^2; B (U_now(i,j1) - 2*U_now(i,j) U_now(i,j-1)) / params.delta_z^2; U_next(i,j) 2*U_now(i,j) - U_prev(i,j) v_model(i,j).^2 .* (A B) * params.delta_t^2; % 添加震源 U_next(params.Sx, params.Sz) gaussian_source(params.f0, time); % 吸收边界处理 absorb_coef 0.3; U_next apply_absorbing_boundary(U_next, absorb_coef); % 更新波场 U_prev U_now; U_now U_next; % 记录地震数据地表 seismogram(:, step) U_now(:, 1); % 定期保存快照 if mod(step, 50) 0 snapshots{snapshot_idx} U_now; snapshot_idx snapshot_idx 1; end end end function U apply_absorbing_boundary(U, coef) % 应用吸收边界条件 CNX size(U, 1); CNZ size(U, 2); % 上下边界 U(:, 1) U(:, 2) * (1 - coef); U(:, CNZ) U(:, CNZ-1) * (1 - coef); % 左右边界 U(1, :) U(2, :) * (1 - coef); U(CNX, :) U(CNX-1, :) * (1 - coef); end function src gaussian_source(f0, time) % 高斯脉冲震源函数 src 5.76*f0^2*(1-16*(0.6*f0*time-1)^2)*exp(-8*(0.6*f0*time-1)^2); end function visualize_results(params, seismogram, snapshots) % 绘制地震记录 figure; imagesc(seismogram); xlabel(Time Step); ylabel(Receiver Position); title(Seismogram); colorbar; % 创建波动动画 figure; for i 1:length(snapshots) surf(snapshots{i}); shading interp; view(2); colormap(jet); caxis([-1 1]*max(abs(snapshots{i}(:)))); title(sprintf(Wavefield at step %d, i*50)); drawnow; end end