SG-PML吸收边界条件(边界反射问题)
SG-PML吸收边界条件(边界反射问题)
本文将对博文 波动方程的有限差分解(地震数据正演) 中提到的边界反射问题进行研究。解决地震正演中的边界反射带来的干扰。
首先我们要知道波为什么会反射,当从一种介质进入另一种介质时,在介质边界由于阻抗的不连续性,导致一部分能量继续进入新的介质称为透射波,一部分能量返回原介质称为反射波。而我们将需要了解反射部分具体是多少,以及如何消除边界反射问题。
吸收边界条件
自然界中地震波传播到介质边界(中断层)时,界面的波阻抗出现差异导致波发生反射和透射,地球介质可以看作“无限大”的区域,但在模拟计算中受到计算量的限制,模拟的传播区域大小是由界限的,这个人为截取的区域便是人工边界。
在人工边界处,边界外的波速可以看为0,即边界反射系数绝对值为1,此时表现在数值模拟中便产生了全反射现象,对地震正演中的波场计算将产生影响。此时我们需要引入吸收边界的概念来吸收掉这些传播至边界的波。
吸收边界条件即人为的在模拟计算区域的边界处设置一定厚度的吸收层,让能量在吸收层呈指数曲线衰减,最终在吸收层边界处近似衰减到0而避免边界反射。
注意这里我们的衰减函数也是有讲究的,因为如果衰减不够连续的话吸收层也将看成断层产生反射,虽然这里使用指数曲线进行衰减,但是实际模拟时还是会产生一定量的反射波。
PML
- PML边界条件将波场分裂为垂直和平行的PML边界两部分,通过再分裂向量满足的微分方程中添加阻尼项得到PML中的微分方程。同时renger证明了如果不进行离散化PML条件可以完全无反射地吸收所有入射波。当然我们的模拟实现是没办法做到这一点的。
一维声波方程
这里我们先以一维声波为例说明PML边界条件思想,一维声波在均匀介质中的波动方程为[1]
其中代表速度,代表波信息(即位移),我们添加一个中间变量将上述方程改写为两个一阶偏微分方程
显然式2再对求偏导即得式1,此时我们可以得到一个PML控制方程组
其中代表式3方程的解,代表衰减系数,我们后续步骤的目的便是研究这个PML控制方程的解和原解的关系。对式3做关于时间的傅里叶变换得
其中分别代表的傅里叶变换。再将实数空间域转换到复数空间域后的坐标得
则有
将式6带入式4得
再对此式子做傅里叶逆变换到时间域上得
可以发现式8与式2形式完全相同,设式2的解为,式8的解为,则式3的解为
这代表的含义为我们可以先求式2的解(有限差分或者其他方法),再对解做坐标变换便可以得到式3的解。同时对于式2在均匀介质中有如下特解
则对应的式3中的解为
将式5带入得
可得上述解得振幅比(阻尼系数)为
这代表引入后波信号将按传播距离指数衰减。
交错网格
在求解二维声波方程的PML控制方程前,我们先来说明一下交错网格[2]的概念,在求解高维时,我们会将波场分裂为各个维度的分量,例如二维中的方向和方向,将二阶微分方程变成两个一阶速度-应力方程进行求解。
对于二维声波方程,设分别为方向的波场位移分量,则对和方向的中心差分方程为
其中为差分系数(通过泰勒展开推导),为差分空间阶数。
由式3有
设右式经过空间差分后近似结果为,其中代表第时刻。
则在交错网格下的PML控制方程近似方案如下
带入式14得
二维声波方程
那对于二维也是同理,对于二维声波方程
将分解为两个方向得分量,转为一阶速度——应力方程为
对应的PML控制方程为
空间上按照式13,时间上按照式16进行差分近似,得到交错网格有限差分声波方程时间递推方程为
衰减系数
在PML中衰减系数满足,且在模拟时我们可以将PML区域分为三种不同情况
对于图1中区域2满足,即垂直衰减水平方向不衰减,对于区域3则满足。而对于区域1则满足。
一般选择[3]
其中为PML的厚度,是理论反射系数中入射角的情况,有
其中为波速,是啥我也不太清楚。当取时则有
正演实验
先通过波场快照的动画,看看吸收效果。这里依然使用的是FCNVMB中的速度模型
结果代码from tqdm import tqdm import numpy as np import matplotlib.pylab as plt import matplotlib.animation as animation import scipy.io data = scipy.io.loadmat(r'vmodel20.mat') vmodel = data['vmodel'] h = 5 # PML宽度 nodr = 3 # 空间差分阶数/2 nz, nx = vmodel.shape wave_source_ampl = 1 Nz, Nx = nz + 2 * h, nx + 2 * h dx = dz = 10 dt = 0.001 nt = 1000 # 差分系数 B = np.zeros([nodr, 1]) B[0][0] = 1 A = np.zeros([nodr, nodr]) for i in range(nodr): A[i, :] = np.power(np.arange(1, 2 * nodr, 2), 2 * i + 1) C = np.dot(np.linalg.inv(A), B).reshape(-1) # 密度 rho = 10 * np.ones([Nz, Nx]) # 这里用均匀密度模型 vmodel_pad = np.pad(vmodel, [h, h], 'edge') # 震源 sx = nx//2 sz = 0 f0 = 25. t = dt * np.arange(1, nt + 1, 1) t0 = 1 / f0 source_wav = (1 - 2 * np.power((np.pi * f0 * (t - t0)), 2)) * np.exp(-np.power((np.pi * f0 * (t - t0)), 2)) # 衰减系数 R = 0.001 # 理论反射系数 v_max = np.max(vmodel_pad) dp_z = np.zeros([Nz, Nx]) dp_x = np.zeros([Nz, Nx]) dp0_z = -np.log(R)*3*v_max/(2*h*dz) dp_z[:h, :] = np.dot(dp0_z * np.power(np.arange(h, 0, -1) / h, 2).reshape(-1, 1), np.ones([1, Nx])) dp_z[(Nz - h):, :] = dp_z[h - 1::-1, :] dp0_x = -np.log(R)*3*v_max/(2*h*dx) dp_x[:, :h] = np.dot(np.ones([Nz, 1]), dp0_x * np.power(np.arange(h, 0, -1) / h, 2).reshape(1, -1)) dp_x[:, (Nx - h):] = dp_x[:, (h - 1)::-1] # PML系数 rho1 = rho.copy() rho2 = rho.copy() Coeffi1 = (1 - dt * dp_x) Coeffi2 = (1 - dt * dp_z) Coeffi3 = 1 / rho1 / dx * dt Coeffi4 = 1 / rho2 / dz * dt Coeffi5 = rho * np.power(vmodel_pad, 2) / dx * dt Coeffi6 = rho * np.power(vmodel_pad, 2) / dz * dt NZ = Nz + 2 * nodr NX = Nx + 2 * nodr Znodes = np.arange(nodr, NZ - nodr, 1) Xnodes = np.arange(nodr, NX - nodr, 1) znodes = np.arange(nodr + h, nodr + h + nz) xnodes = np.arange(nodr + h, nodr + h + nx) sz_pad = nodr + h + sz sx_pad = nodr + h + sx Ut = np.zeros([NZ, NX]) Uz = np.zeros([NZ, NX]) Ux = np.zeros([NZ, NX]) Vz = np.zeros([NZ, NX]) Vx = np.zeros([NZ, NX]) U = -1 * np.ones([nz, nx, nt]) Psum = -1 * np.ones([Nz, Nx]) def index_2dim(matrix, height_index_array, width_index_array): return matrix[height_index_array[:, np.newaxis], width_index_array] for cur_time in tqdm(range(nt)): Ux[sz_pad, sx_pad] = Ux[sz_pad, sx_pad] + wave_source_ampl * source_wav[cur_time] / 2 Uz[sz_pad, sx_pad] = Uz[sz_pad, sx_pad] + wave_source_ampl * source_wav[cur_time] / 2 Ut = Ux + Uz # Ut is the combination of the two component matrices Ux and Uz U[:, :, cur_time] = index_2dim(Ut, znodes, xnodes) Psum[:, :] = 0 for i in range(1, nodr + 1): Psum = Psum + C[i - 1] * (index_2dim(Ut, Znodes, Xnodes + i) - index_2dim(Ut, Znodes, Xnodes + 1 - i)) Vx[nodr:NZ - nodr, nodr:NX - nodr] = Coeffi1 * index_2dim(Vx, Znodes, Xnodes) - Coeffi3 * Psum Psum[:, :] = 0 for i in range(1, nodr + 1): Psum = Psum + C[i - 1] * (index_2dim(Ut, Znodes + i, Xnodes) - index_2dim(Ut, Znodes + 1 - i, Xnodes)) Vz[nodr:NZ - nodr, nodr:NX - nodr] = Coeffi2 * index_2dim(Vz, Znodes, Xnodes) - Coeffi4 * Psum Psum[:, :] = 0 for i in range(1, nodr + 1): Psum = Psum + C[i - 1] * (index_2dim(Vx, Znodes, Xnodes - 1 + i) - index_2dim(Vx, Znodes, Xnodes - i)) Ux[nodr:NZ - nodr, nodr:NX - nodr] = Coeffi1 * index_2dim(Ux, Znodes, Xnodes) - Coeffi5 * Psum Psum[:, :] = 0 for i in range(1, nodr + 1): Psum = Psum + C[i - 1] * (index_2dim(Vz, Znodes - 1 + i, Xnodes) - index_2dim(Vz, Znodes - i, Xnodes)) Uz[nodr:NZ - nodr, nodr:NX - nodr] = Coeffi2 * index_2dim(Uz, Znodes, Xnodes) - Coeffi6 * Psum fig, ax = plt.subplots() im = ax.imshow(U[:, :, 0], extent=[0, Nx, Nz, 0], cmap='seismic', aspect='auto', vmin=-0.4, vmax=0.44) ax.set_xlabel('X (m)') ax.set_ylabel('Z (m)') ax.set_title('Wavefield Animation') fig.colorbar(im, ax=ax, label='Amplitude') def init(): im.set_data(U[:, :, 0]) return im, def update(frame): im.set_data(U[:, :, frame]) return im, ani = animation.FuncAnimation(fig, update, frames=range(1, nt), init_func=init, blit=True, interval=20) plt.show()
通过图2可以看出吸收效果还是非常好的,然后我们在地面设置检波器进行正演。
可以看到效果相比[4]中没有吸收边界的情况好太多了。