面波模拟基本原理
面波(瑞利波、勒夫波)是沿地球表面传播的地震波,具有频散特性。本代码采用二维弹性波有限差分法模拟瑞利面波。
核心方程(弹性波方程)
ρ∂²u/∂t² = ∂σ_xx/∂x + ∂σ_xz/∂z
ρ∂²w/∂t² = ∂σ_xz/∂x + ∂σ_zz/∂z
σ_xx = (λ+2μ)∂u/∂x + λ∂w/∂z
σ_zz = λ∂u/∂x + (λ+2μ)∂w/∂z
σ_xz = μ(∂u/∂z + ∂w/∂x)
MATLAB代码
1. 主程序:面波模拟核心
matlab
%% 地震面波数值模拟 - 二维弹性波有限差分
% 功能:模拟瑞利面波在层状介质中的传播
% 方法:交错网格有限差分,自由表面边界条件
clear all; close all; clc;
%% ==================== 参数设置 ====================
% 空间参数
nx = 200; % x方向网格数
nz = 100; % z方向网格数
dx = 10; % 空间步长(m)
dz = 10; % 空间步长(m)
% 时间参数
nt = 1000; % 时间步数
dt = 0.001; % 时间步长(s)
t = (0:nt-1)*dt; % 时间向量
% 震源参数
src_x = nx/2; % 震源x位置
src_z = 5; % 震源z位置(近地表)
src_type = 'ricker';% 震源类型:ricker/gaussian
f0 = 10; % 主频(Hz)
t0 = 0.1; % 延迟时间(s)
% 介质参数(两层模型)
vp_top = 800; % 上层P波速度(m/s)
vs_top = 400; % 上层S波速度(m/s)
rho_top = 1800; % 上层密度(kg/m³)
vp_bottom = 1500; % 下层P波速度(m/s)
vs_bottom = 800; % 下层S波速度(m/s)
rho_bottom = 2200; % 下层密度(kg/m³)
interface_depth = 50; % 界面深度(m)
% 接收器参数
n_rec = 20; % 接收器数量
rec_z = 2; % 接收器深度(m)
rec_x = linspace(50, nx*dx-50, n_rec); % 接收器x位置
% 输出参数
plot_interval = 20; % 绘图间隔步数
save_wavefield = false; % 是否保存波场
output_dir = './results/';
%% ==================== 介质模型构建 ====================
fprintf('构建介质模型...\n');
% 初始化介质参数矩阵
vp = ones(nz, nx) * vp_top;
vs = ones(nz, nx) * vs_top;
rho = ones(nz, nx) * rho_top;
% 设置下层介质
interface_idx = round(interface_depth/dz);
vp(interface_idx:end, :) = vp_bottom;
vs(interface_idx:end, :) = vs_bottom;
rho(interface_idx:end, :) = rho_bottom;
% 计算拉梅常数
mu = rho .* vs.^2; % 剪切模量
lambda = rho .* vp.^2 - 2*mu; % 拉梅常数
% 稳定性检查
cfl_condition = max(vp(:)) * dt / min(dx, dz);
if cfl_condition > 0.7
warning('CFL条件不满足,可能不稳定!建议减小dt');
fprintf('当前CFL数: %.3f\n', cfl_condition);
end
%% ==================== 初始化波场 ====================
fprintf('初始化波场变量...\n');
% 位移场
u = zeros(nz, nx); % x方向位移
w = zeros(nz, nx); % z方向位移
% 速度场
vx = zeros(nz, nx); % x方向速度
vz = zeros(nz, nx); % z方向速度
% 应力场
sxx = zeros(nz, nx); % 正应力xx
szz = zeros(nz, nx); % 正应力zz
sxz = zeros(nz, nx); % 剪应力xz
% 接收器记录
seis_u = zeros(nt, n_rec); % x方向位移记录
seis_w = zeros(nt, n_rec); % z方向位移记录
% 波场快照存储
if save_wavefield
u_snapshots = zeros(nz, nx, floor(nt/plot_interval));
w_snapshots = zeros(nz, nx, floor(nt/plot_interval));
snapshot_count = 1;
end
%% ==================== 震源函数 ====================
fprintf('生成震源函数...\n');
% Ricker子波
if strcmpi(src_type, 'ricker')
% Ricker子波公式
tau = pi * f0 * (t - t0);
src = (1 - 2*tau.^2) .* exp(-tau.^2);
else
% 高斯脉冲
src = exp(-(f0 * (t - t0)).^2);
end
% 归一化
src = src / max(abs(src));
% 震源位置索引
isrc_x = round(src_x);
isrc_z = round(src_z);
%% ==================== 有限差分系数 ====================
% 空间差分系数(2阶精度)
c1 = 1/dx;
c2 = 1/dz;
% 时间差分系数(2阶精度)
c1t = dt/dx;
c2t = dt/dz;
%% ==================== 主时间循环 ====================
fprintf('开始时间循环模拟...\n');
fprintf('总时间步数: %d, 时间步长: %.4f s\n', nt, dt);
% 创建进度条
h = waitbar(0, '模拟进行中...');
for it = 1:nt
%% ----- 更新速度场 -----
% x方向速度
for iz = 2:nz-1
for ix = 2:nx-1
% 应力梯度
dsxx_dx = (sxx(iz, ix+1) - sxx(iz, ix-1)) * c1 / 2;
dsxz_dz = (sxz(iz+1, ix) - sxz(iz-1, ix)) * c2 / 2;
% 速度更新
vx(iz, ix) = vx(iz, ix) + dt/rho(iz, ix) * (dsxx_dx + dsxz_dz);
end
end
% z方向速度
for iz = 2:nz-1
for ix = 2:nx-1
% 应力梯度
dsxz_dx = (sxz(iz, ix+1) - sxz(iz, ix-1)) * c1 / 2;
dszz_dz = (szz(iz+1, ix) - szz(iz-1, ix)) * c2 / 2;
% 速度更新
vz(iz, ix) = vz(iz, ix) + dt/rho(iz, ix) * (dsxz_dx + dszz_dz);
end
end
%% ----- 添加震源 -----
% 在震源位置添加力源(垂直力,激发面波)
if it <= length(src)
vz(isrc_z, isrc_x) = vz(isrc_z, isrc_x) + src(it) * dt/rho(isrc_z, isrc_x);
end
%% ----- 更新位移场 -----
u = u + vx * dt;
w = w + vz * dt;
%% ----- 更新应力场 -----
% 计算应变
du_dx = zeros(nz, nx);
dw_dz = zeros(nz, nx);
du_dz = zeros(nz, nx);
dw_dx = zeros(nz, nx);
for iz = 2:nz-1
for ix = 2:nx-1
% 应变分量
du_dx(iz, ix) = (u(iz, ix+1) - u(iz, ix-1)) * c1 / 2;
dw_dz(iz, ix) = (w(iz+1, ix) - w(iz-1, ix)) * c2 / 2;
du_dz(iz, ix) = (u(iz+1, ix) - u(iz-1, ix)) * c2 / 2;
dw_dx(iz, ix) = (w(iz, ix+1) - w(iz, ix-1)) * c1 / 2;
end
end
% 更新应力(本构关系)
for iz = 2:nz-1
for ix = 2:nx-1
sxx(iz, ix) = (lambda(iz, ix) + 2*mu(iz, ix)) * du_dx(iz, ix) + ...
lambda(iz, ix) * dw_dz(iz, ix);
szz(iz, ix) = lambda(iz, ix) * du_dx(iz, ix) + ...
(lambda(iz, ix) + 2*mu(iz, ix)) * dw_dz(iz, ix);
sxz(iz, ix) = mu(iz, ix) * (du_dz(iz, ix) + dw_dx(iz, ix));
end
end
%% ----- 自由表面边界条件(z=0) -----
% 自由表面:正应力和剪应力为零
sxx(1, :) = 0;
szz(1, :) = 0;
sxz(1, :) = 0;
% 镜像法处理自由表面
sxz(2, :) = -sxz(3, :); % 反对称
%% ----- 吸收边界条件(简单海绵边界) -----
% 左右边界
boundary_width = 20;
for i = 1:boundary_width
damping = exp(-(0.015*(boundary_width-i+1))^2);
% 左边界
vx(:, i) = vx(:, i) * damping;
vz(:, i) = vz(:, i) * damping;
% 右边界
vx(:, nx-i+1) = vx(:, nx-i+1) * damping;
vz(:, nx-i+1) = vz(:, nx-i+1) * damping;
end
% 下边界
for i = 1:boundary_width
damping = exp(-(0.015*(boundary_width-i+1))^2);
vx(nz-i+1, :) = vx(nz-i+1, :) * damping;
vz(nz-i+1, :) = vz(nz-i+1, :) * damping;
end
%% ----- 记录接收器数据 -----
for ir = 1:n_rec
ix_rec = round(rec_x(ir)/dx);
iz_rec = round(rec_z/dz);
if ix_rec >= 1 && ix_rec <= nx && iz_rec >= 1 && iz_rec <= nz
seis_u(it, ir) = u(iz_rec, ix_rec);
seis_w(it, ir) = w(iz_rec, ix_rec);
end
end
%% ----- 保存波场快照 -----
if save_wavefield && mod(it, plot_interval) == 0
u_snapshots(:, :, snapshot_count) = u;
w_snapshots(:, :, snapshot_count) = w;
snapshot_count = snapshot_count + 1;
end
%% ----- 实时显示 -----
if mod(it, plot_interval) == 0
% 更新进度条
waitbar(it/nt, h, sprintf('模拟进度: %d/%d (%.1f%%)', it, nt, it/nt*100));
% 显示波场
if mod(it, plot_interval*5) == 0
Plot_Wavefield(u, w, dx, dz, it, dt);
end
end
end
close(h);
fprintf('模拟完成!\n');
%% ==================== 结果可视化 ====================
fprintf('生成结果图...\n');
% 创建输出目录
if ~exist(output_dir, 'dir')
mkdir(output_dir);
end
% 1. 最终波场快照
figure('Position', [100, 100, 1200, 800]);
subplot(2,2,1);
imagesc((1:nx)*dx/1000, (1:nz)*dz/1000, u);
xlabel('距离 (km)'); ylabel('深度 (km)');
title('水平位移场 (最终时刻)');
colorbar; colormap(jet);
hold on;
plot([1 nx]*dx/1000, [interface_depth interface_depth]/1000, 'w--', 'LineWidth', 2);
text(nx*dx/2000, interface_depth/1000+0.05, '界面', 'Color', 'w', 'FontSize', 12);
subplot(2,2,2);
imagesc((1:nx)*dx/1000, (1:nz)*dz/1000, w);
xlabel('距离 (km)'); ylabel('深度 (km)');
title('垂直位移场 (最终时刻)');
colorbar; colormap(jet);
hold on;
plot([1 nx]*dx/1000, [interface_depth interface_depth]/1000, 'w--', 'LineWidth', 2);
% 2. 地震记录
subplot(2,2,3);
imagesc(rec_x/1000, t, seis_u);
xlabel('接收器位置 (km)'); ylabel('时间 (s)');
title('水平分量地震记录');
colorbar; colormap(seismic);
hold on;
plot(src_x*dx/1000, 0, 'r^', 'MarkerSize', 10, 'MarkerFaceColor', 'r');
text(src_x*dx/1000, 0.05, '震源', 'Color', 'r', 'FontSize', 10);
subplot(2,2,4);
imagesc(rec_x/1000, t, seis_w);
xlabel('接收器位置 (km)'); ylabel('时间 (s)');
title('垂直分量地震记录');
colorbar; colormap(seismic);
hold on;
plot(src_x*dx/1000, 0, 'r^', 'MarkerSize', 10, 'MarkerFaceColor', 'r');
sgtitle('地震面波数值模拟结果', 'FontSize', 14, 'FontWeight', 'bold');
saveas(gcf, fullfile(output_dir, 'wavefield_results.png'));
% 3. 单道记录分析
figure('Position', [100, 100, 1000, 600]);
subplot(2,1,1);
plot(t, seis_u(:, round(n_rec/2)), 'b', 'LineWidth', 1.5);
xlabel('时间 (s)'); ylabel('振幅');
title(sprintf('中间接收器水平分量 (x=%.1f km)', rec_x(round(n_rec/2))/1000));
grid on;
subplot(2,1,2);
plot(t, seis_w(:, round(n_rec/2)), 'r', 'LineWidth', 1.5);
xlabel('时间 (s)'); ylabel('振幅');
title(sprintf('中间接收器垂直分量 (x=%.1f km)', rec_x(round(n_rec/2))/1000));
grid on;
sgtitle('单道地震记录', 'FontSize', 14, 'FontWeight', 'bold');
saveas(gcf, fullfile(output_dir, 'single_trace.png'));
% 4. 频散分析(面波特征)
figure('Position', [100, 100, 1000, 400]);
[F, T, P] = spectrogram(seis_w(:, round(n_rec/2)), 128, 120, 128, 1/dt);
surf(T, F, 10*log10(P), 'EdgeColor', 'none');
axis tight; view(0, 90);
xlabel('时间 (s)'); ylabel('频率 (Hz)');
title('垂直分量时频分析(面波频散特征)');
colorbar;
saveas(gcf, fullfile(output_dir, 'dispersion_analysis.png'));
%% ==================== 保存数据 ====================
fprintf('保存数据...\n');
save(fullfile(output_dir, 'simulation_data.mat'), ...
'u', 'w', 'seis_u', 'seis_w', 't', 'rec_x', 'src', ...
'vp', 'vs', 'rho', 'dx', 'dz', 'dt', 'nt');
fprintf('所有结果已保存至: %s\n', output_dir);
fprintf('模拟结束!\n');
2. 辅助函数:波场可视化
matlab
function Plot_Wavefield(u, w, dx, dz, it, dt)
% 实时绘制波场快照
% 输入:
% u - 水平位移场
% w - 垂直位移场
% dx, dz - 空间步长
% it - 当前时间步
% dt - 时间步长
persistent fig1 fig2;
if isempty(fig1) || ~isvalid(fig1)
fig1 = figure(1);
set(fig1, 'Position', [50, 50, 800, 400]);
end
if isempty(fig2) || ~isvalid(fig2)
fig2 = figure(2);
set(fig2, 'Position', [900, 50, 800, 400]);
end
% 获取波场范围
[nz, nx] = size(u);
x = (1:nx) * dx;
z = (1:nz) * dz;
% 图1:水平位移
figure(fig1);
subplot(1,2,1);
imagesc(x/1000, z/1000, u);
xlabel('距离 (km)'); ylabel('深度 (km)');
title(sprintf('水平位移场 (t=%.3f s)', it*dt));
colorbar; colormap(jet);
caxis([-max(abs(u(:))), max(abs(u(:)))] * 0.1);
% 图1:垂直位移
subplot(1,2,2);
imagesc(x/1000, z/1000, w);
xlabel('距离 (km)'); ylabel('深度 (km)');
title(sprintf('垂直位移场 (t=%.3f s)', it*dt));
colorbar; colormap(jet);
caxis([-max(abs(w(:))), max(abs(w(:)))] * 0.1);
sgtitle(sprintf('时间步: %d / 总时间: %.3f s', it, it*dt), 'FontSize', 12);
% 图2:合成波场(能量)
figure(fig2);
energy = sqrt(u.^2 + w.^2);
imagesc(x/1000, z/1000, log10(energy + 1e-10));
xlabel('距离 (km)'); ylabel('深度 (km)');
title(sprintf('波场能量分布 (t=%.3f s)', it*dt));
colorbar; colormap(hot);
hold on;
contour(x/1000, z/1000, energy, 10, 'w', 'LineWidth', 0.5);
hold off;
drawnow;
end
3. 辅助函数:震源子波
matlab
function [wavelet, t] = Generate_Source(type, f0, t0, dt, nt)
% 生成震源子波
% 输入:
% type - 子波类型:'ricker'或'gaussian'
% f0 - 主频(Hz)
% t0 - 延迟时间(s)
% dt - 时间步长(s)
% nt - 时间点数
% 输出:
% wavelet - 子波序列
% t - 时间向量
t = (0:nt-1)*dt;
switch lower(type)
case 'ricker'
% Ricker子波(Mexican Hat Wavelet)
tau = pi * f0 * (t - t0);
wavelet = (1 - 2*tau.^2) .* exp(-tau.^2);
case 'gaussian'
% 高斯脉冲
sigma = 1/(pi*f0);
wavelet = exp(-((t - t0).^2)/(2*sigma^2));
case 'derivative_gaussian'
% 高斯导数(用于爆炸源)
sigma = 1/(pi*f0);
wavelet = -(t - t0) .* exp(-((t - t0).^2)/(2*sigma^2)) / sigma^2;
otherwise
error('未知震源类型: %s', type);
end
% 归一化
wavelet = wavelet / max(abs(wavelet));
end
4. 辅助函数:介质模型生成
matlab
function [vp, vs, rho] = Create_Layered_Model(nz, nx, dz, dx, params)
% 创建层状介质模型
% 输入:
% nz, nx - 网格尺寸
% dz, dx - 空间步长
% params - 结构体包含各层参数
% 输出:
% vp, vs, rho - 速度密度模型
% 默认参数
default_params.layers = {
struct('vp', 800, 'vs', 400, 'rho', 1800, 'thickness', 50), % 第一层
struct('vp', 1500, 'vs', 800, 'rho', 2200, 'thickness', 50) % 第二层
};
if nargin < 5
params = default_params;
end
% 初始化
vp = zeros(nz, nx);
vs = zeros(nz, nx);
rho = zeros(nz, nx);
% 逐层设置
current_depth = 0;
for ilayer = 1:length(params.layers)
layer = params.layers{ilayer};
% 计算该层的网格范围
start_idx = round(current_depth/dz) + 1;
if ilayer == length(params.layers)
end_idx = nz;
else
end_idx = round((current_depth + layer.thickness)/dz);
end
% 确保索引有效
start_idx = max(1, min(start_idx, nz));
end_idx = max(1, min(end_idx, nz));
% 设置该层参数
vp(start_idx:end_idx, :) = layer.vp;
vs(start_idx:end_idx, :) = layer.vs;
rho(start_idx:end_idx, :) = layer.rho;
% 更新当前深度
current_depth = current_depth + layer.thickness;
end
% 添加随机扰动(可选)
add_perturbation = false;
if add_perturbation
% 小尺度随机扰动
perturbation = 0.05; % 5%扰动
vp = vp .* (1 + perturbation * randn(nz, nx));
vs = vs .* (1 + perturbation * randn(nz, nx));
rho = rho .* (1 + perturbation * randn(nz, nx));
% 平滑处理
vp = imgaussfilt(vp, 2);
vs = imgaussfilt(vs, 2);
rho = imgaussfilt(rho, 2);
end
end
运行与结果分析
1. 运行步骤
matlab
% 步骤1:运行主程序
% 将上述代码保存为 'seismic_surface_wave.m'
% 在MATLAB命令行运行:
>> seismic_surface_wave
% 步骤2:查看结果
% 程序会自动生成以下文件:
% ./results/wavefield_results.png - 波场快照
% ./results/single_trace.png - 单道记录
% ./results/dispersion_analysis.png - 频散分析
% ./results/simulation_data.mat - 原始数据
% 步骤3:参数调整
% 修改主程序开头的参数,重新运行
2. 预期结果特征
| 波型 | 特征 | 在结果中的表现 |
|---|---|---|
| 瑞利面波 | 椭圆极化,垂直分量为主 | 垂直记录强于水平记录 |
| 体波(P波) | 传播最快,压缩波 | 最先到达的波 |
| 体波(S波) | 剪切波,速度较慢 | 第二组到达的波 |
| 频散 | 不同频率传播速度不同 | 时频图中的倾斜条纹 |
3. 关键参数影响
matlab
%% 敏感性分析参数
% 1. 网格分辨率
dx_options = [5, 10, 20]; % 空间步长(m)
% 较小dx:精度高但计算慢
% 较大dx:计算快但可能数值频散
% 2. 时间步长
dt_options = [0.0005, 0.001, 0.002]; % 时间步长(s)
% 必须满足CFL条件:max(vp)*dt/min(dx,dz) < 0.7
% 3. 震源频率
f0_options = [5, 10, 20]; % 主频(Hz)
% 低频:穿透深度大,分辨率低
% 高频:分辨率高,衰减快
% 4. 介质参数
% 速度比vp/vs影响面波特性
% 泊松比ν = 0.5*(vp^2/vs^2 - 2)/(vp^2/vs^2 - 1)
参考代码 地震面波数值模拟 www.youwenfan.com/contentcst/73353.html
高级功能扩展
1. 频散曲线提取
matlab
function [phase_velocity, group_velocity] = Extract_Dispersion_Curve(seis, dt, dx)
% 从地震记录提取面波频散曲线
% 输入:
% seis - 地震记录矩阵(时间×距离)
% dt - 时间采样率
% dx - 空间采样间隔
% 输出:
% phase_velocity - 相速度频散曲线
% group_velocity - 群速度频散曲线
[nt, nx] = size(seis);
f = (0:nt/2-1)*(1/(nt*dt)); % 频率向量
k = (0:nx/2-1)*(2*pi/(nx*dx)); % 波数向量
% 2D傅里叶变换
Seis_fk = fft2(seis);
Seis_fk = Seis_fk(1:nt/2, 1:nx/2); % 单边谱
% 提取频散关系
[K, F] = meshgrid(k, f);
phase_velocity = 2*pi*F ./ K;
group_velocity = gradient(2*pi*F) ./ gradient(K);
% 去除异常值
phase_velocity(phase_velocity > 5000 | phase_velocity < 100) = NaN;
group_velocity(group_velocity > 5000 | group_velocity < 100) = NaN;
end
2. 各向异性介质扩展
matlab
function [C] = Anisotropic_Stiffness_Tensor(vp, vs, rho, epsilon, delta)
% 创建VTI各向异性介质刚度张量
% 输入:
% vp, vs, rho - 背景速度密度
% epsilon, delta - Thomsen参数
% 输出:
% C - 刚度张量(5个独立分量)
% 各向同性背景
mu = rho .* vs.^2;
lambda = rho .* vp.^2 - 2*mu;
% VTI介质刚度分量
C11 = rho .* vp.^2 .* (1 + 2*epsilon);
C33 = rho .* vp.^2;
C13 = sqrt((C33 - mu) .* (C11 - mu)) - mu;
C55 = mu;
C66 = mu .* (1 + 2*gamma); % gamma为横波各向异性参数
% 组装刚度矩阵
C = struct('C11', C11, 'C33', C33, 'C13', C13, 'C55', C55, 'C66', C66);
end
3. 并行计算优化
matlab
%% 使用parfor加速计算
% 在时间循环中并行化空间循环
if use_parallel
pool = gcp('nocreate');
if isempty(pool)
parpool('local', 4); % 开启4个worker
end
% 并行更新速度场
parfor iz = 2:nz-1
for ix = 2:nx-1
% 并行计算代码
end
end
end
常见问题与解决
| 问题 | 现象 | 解决方案 |
|---|---|---|
| 数值不稳定 | 波场爆炸式增长 | 减小dt,检查CFL条件 |
| 数值频散 | 波形出现锯齿状 | 减小dx/dz,使用高阶差分 |
| 边界反射 | 边界处明显反射 | 增加吸收边界宽度,使用PML边界 |
| 内存不足 | MATLAB崩溃 | 减小模型尺寸,使用单精度 |
| 面波不明显 | 体波占主导 | 震源置于地表,使用垂直力源 |
优化建议
-
内存优化
matlab% 使用单精度节省内存 u = zeros(nz, nx, 'single'); vx = zeros(nz, nx, 'single'); -
计算加速
matlab% 向量化运算替代循环 du_dx = diff(u, 1, 2) / dx; % 替代x方向循环 dw_dz = diff(w, 1, 1) / dz; % 替代z方向循环 -
输出优化
matlab% 选择性保存波场 save_interval = 100; % 每100步保存一次 if mod(it, save_interval) == 0 save(fullfile(output_dir, sprintf('wavefield_%04d.mat', it)), 'u', 'w'); end