Skip to content

Commit 41c6686

Browse files
authored
DOC: “波形在漂移”的成因以及解决方法 (#162)
1 parent 8526ee6 commit 41c6686

File tree

8 files changed

+289
-5
lines changed

8 files changed

+289
-5
lines changed

docs/source/Advanced/index.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
:author: 朱邓达
22
:date: 2025-04-23
3-
:updated_date: 2025-05-05
3+
:updated_date: 2025-12-05
44

55
进阶教程
66
=============
@@ -15,3 +15,4 @@
1515
filon/self_adaptive_filon
1616
integ_converg/integ_converg
1717
kernel/kernel
18+
waveform_drift/waveform_drift
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
0.2 3.4 1.7 2.3
2+
0.6 3.7 1.9 2.4
3+
0.5 4.2 2.1 2.4
4+
0.5 4.6 2.3 2.5
5+
0.7 4.9 2.8 2.6
6+
0.5 5.1 2.9 2.7
7+
6.0 5.9 3.3 2.7
8+
28 6.9 4.0 2.8
9+
0.0 8.2 4.7 3.2
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# BEGIN 1
2+
import numpy as np
3+
import pygrt
4+
5+
modarr = np.loadtxt("milrow")
6+
7+
pymod = pygrt.PyModel1D(modarr, depsrc=10, deprcv=0.0)
8+
9+
nt = 500
10+
dt = 10
11+
12+
st_grn = pymod.compute_grn(5000, nt=nt, dt=dt, keepAllFreq=True, statsfile="pygrtstats")[0]
13+
# END 1
14+
15+
# 仅绘制一个分量做示例
16+
CHANNEL = "SSR"
17+
18+
# =================================================================
19+
# 绘制波形
20+
import matplotlib.pyplot as plt
21+
from obspy import *
22+
plt.rcParams.update({
23+
"font.sans-serif": "Times New Roman",
24+
"mathtext.fontset": "cm"
25+
})
26+
27+
t = np.arange(nt)*dt
28+
freqs = np.arange(nt//2 + 1)/(nt*dt)
29+
30+
def plot_waves(tr:Trace):
31+
fig, ax = plt.subplots(figsize=(8, 1.5))
32+
ax.plot(t, tr.data, 'k-', lw=0.5)
33+
ax.grid()
34+
ax.text(0.02, 0.9, CHANNEL, transform=ax.transAxes, fontsize=10, ha='left', va='top', bbox=dict(lw=1, fc='w'))
35+
ax.ticklabel_format(axis='y', style='sci', scilimits=(0,0))
36+
ax.set_xlim(0, nt*dt)
37+
ax.set_xlabel("Time (s)", fontsize=12)
38+
return fig, ax
39+
40+
fig, ax = plot_waves(st_grn.select(channel=CHANNEL)[0])
41+
fig.savefig("grn.png", dpi=300, bbox_inches='tight')
42+
# =================================================================
43+
44+
# =================================================================
45+
# 回退虚频率补偿
46+
st_grn2 = st_grn.copy()
47+
wI = st_grn2[0].stats.sac['user0']
48+
for tr in st_grn2:
49+
tr.data[:] *= np.exp(-t*wI)
50+
51+
fig, ax = plot_waves(st_grn2.select(channel=CHANNEL)[0])
52+
fig.savefig("grn2.png", dpi=300, bbox_inches='tight')
53+
# =================================================================
54+
55+
56+
# =================================================================
57+
# 绘制频谱
58+
from scipy.fft import rfft, irfft
59+
60+
def plot_freqs(tr:Trace):
61+
fig, ax = plt.subplots(figsize=(8, 1.5))
62+
data = tr.data.copy()
63+
64+
freqs0 = freqs.copy()
65+
freqs0[0] *= 0.1
66+
ax.plot(freqs0[:-1], np.abs(rfft(data))[:-1], 'k-', lw=0.8)
67+
ax.grid()
68+
ax.text(0.02, 0.9, CHANNEL, transform=ax.transAxes, fontsize=10, ha='left', va='top', bbox=dict(lw=1, fc='w'))
69+
ax.ticklabel_format(axis='y', style='sci', scilimits=(0,0))
70+
71+
ax.set_xscale('log')
72+
ax.set_yscale('log')
73+
ax.set_xlim(1e-4, 0.5/dt)
74+
ax.set_xlabel("Frequency (Hz)", fontsize=12)
75+
76+
return fig, ax
77+
78+
fig, ax = plot_freqs(st_grn.select(channel=CHANNEL)[0])
79+
fig.savefig("grn_freqs.png", dpi=300, bbox_inches='tight')
80+
fig, ax = plot_freqs(st_grn2.select(channel=CHANNEL)[0])
81+
fig.savefig("grn2_freqs.png", dpi=300, bbox_inches='tight')
82+
# =================================================================
83+
84+
85+
# =================================================================
86+
# 读入核函数
87+
import glob
88+
paths = glob.glob("pygrtstats/K_000[0-5]_*")
89+
paths.sort()
90+
print(paths)
91+
92+
fig, axs = plt.subplots(len(paths), 1, figsize=(8, len(paths)*1), gridspec_kw=dict(hspace=0), sharex=True)
93+
for i in range(len(paths)):
94+
ax = axs[i]
95+
path = paths[i]
96+
freq = float(path.split("_")[-1])
97+
D = pygrt.utils.read_statsfile(path)
98+
ax.plot(D['k'], np.real(D['SS_q']), 'k-', lw=1, label='Real')
99+
ax.plot(D['k'], np.imag(D['SS_q']), 'k--', lw=1, label='Imag')
100+
ax.text(0.98, 0.9, f"{freq:.1e} Hz", transform=ax.transAxes, fontsize=10, ha='right', va='top', bbox=dict(lw=1, fc='w'))
101+
ax.grid()
102+
ax.set_xlim(xmax=D['k'][-1])
103+
if i == 0:
104+
ax.legend(loc='lower center', ncols=2, bbox_to_anchor=(0.5, 1.05))
105+
106+
fig.savefig("kernels.png", dpi=300, bbox_inches='tight')
107+
# =================================================================
108+
109+
110+
111+
# =================================================================
112+
# 跳过频段,重新计算
113+
st_grn3 = pymod.compute_grn(5000, nt=nt, dt=dt)[0]
114+
115+
srctypes = ['EX', 'VF', 'HF', 'DD', 'DS', 'SS']
116+
chlst = ['Z', 'R', 'T']
117+
def plot_all_waves(st_grn:Stream):
118+
119+
fig = plt.figure(figsize=(14, 2*len(srctypes)))
120+
gs = fig.add_gridspec(len(srctypes), 3, hspace=0.3, wspace=0.1)
121+
for ik, tr in enumerate(st_grn):
122+
channel = tr.stats.channel
123+
srctype = channel[:2]
124+
ch = channel[-1]
125+
126+
ax = fig.add_subplot(gs[srctypes.index(srctype), chlst.index(ch)])
127+
128+
data = tr.data.copy()
129+
130+
ax.plot(t, data, 'k-', lw=0.6)
131+
ax.grid()
132+
ax.text(0.1, 0.9, channel, transform=ax.transAxes, ha='center', va='top', bbox=dict(lw=1, fc='w'))
133+
ax.ticklabel_format(axis='y', style='sci', scilimits=(0,0))
134+
135+
return fig
136+
137+
fig = plot_all_waves(st_grn3.copy())
138+
fig.savefig("grn3.png", dpi=100, bbox_inches='tight')
139+
# =================================================================
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/bash
2+
3+
rm GRN* -rf
4+
5+
# BEGIN 1
6+
grt greenfn -Mmilrow -D10/0 -N500/10+a -R5000 -OGRN -S
7+
# END 1
8+
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
:author: 朱邓达
2+
:date: 2025-12-05
3+
4+
如何解决“波形在漂移”的问题?
5+
================================
6+
7+
问题描述
8+
--------------------
9+
10+
如下所示,以 MILROW 模型为例,
11+
计算震源深度为 10 km,台站在地表,震中距为 5000 km 的格林函数,
12+
采样点数 500,采样间隔 10 秒。仅使用传统的离散波数积分,不使用其他技巧。
13+
14+
脚本下载::download:`run.py <run/run.py>`
15+
16+
MILROW 模型定义如下,与文档中其它部分一致,
17+
18+
.. literalinclude:: run/milrow
19+
:language: text
20+
21+
.. tabs::
22+
23+
.. group-tab:: C
24+
25+
使用如下命令计算格林函数,
26+
27+
.. literalinclude:: run/run.sh
28+
:language: bash
29+
:start-after: BEGIN 1
30+
:end-before: END 1
31+
32+
.. group-tab:: Python
33+
34+
使用如下 Python 脚本计算格林函数,
35+
36+
.. literalinclude:: run/run.py
37+
:language: python
38+
:start-after: BEGIN 1
39+
:end-before: END 1
40+
41+
绘制 SSR 波形如下,发现整个波形发生“漂移”。事实上如果绘制所有格林函数会发现它们都发生了不同程度的漂移。
42+
43+
.. figure:: run/grn.png
44+
:align: center
45+
:width: 100%
46+
47+
问题成因
48+
-----------------
49+
50+
从形态来看,漂移的形态呈 e 指数,很明显是在时域补偿虚频率产生的效果。
51+
52+
回退虚频率的补偿
53+
~~~~~~~~~~~~~~~~~~~~~~~~~~
54+
我们可以将以上波形再乘以 :math:`e^{-\omega_I t}` 以回退虚频率的补偿,观察以下波形形态,
55+
发现整体发生了偏移,即零频结果不准。
56+
57+
.. figure:: run/grn2.png
58+
:align: center
59+
:width: 100%
60+
61+
观察频谱
62+
~~~~~~~~~~~~~~~~
63+
我们再观察频谱, **发现在极低的频段呈现不正常的高值,而虚频率的补偿会进一步放大这一现象,**
64+
这表明不止是零频的一个单独问题,而是一系列低频点发生类似问题。
65+
66+
+ **虚频率补偿前**
67+
68+
.. figure:: run/grn2_freqs.png
69+
:align: center
70+
:width: 100%
71+
72+
+ **虚频率补偿后**
73+
74+
.. figure:: run/grn_freqs.png
75+
:align: center
76+
:width: 100%
77+
78+
观察核函数
79+
~~~~~~~~~~~~~~~~~~~~
80+
低频段计算发生偏差,显然问题出在核函数。上述在计算格林函数时已经指定了相应的参数以保存核函数。
81+
如下,我们可以绘制几个低频段的核函数,发现核函数曲线存在很多毛刺,这就导致了积分结果发生偏差。
82+
83+
.. figure:: run/kernels.png
84+
:align: center
85+
:width: 100%
86+
87+
虚频率的定义
88+
~~~~~~~~~~~~~~~~~~~~~
89+
低频的核函数曲线出现毛刺,这是计算机在做数值计算时发生溢出的表现。
90+
但这个现象在震中距不算太大的情况下并没有出现。事实上,震中距的大小是间接原因,
91+
根本原因是时窗长度。
92+
93+
根据 Bouchon (1981) 的设计,虚频率定义为 :math:`\omega_I = \zeta \dfrac{\pi}{T}` ,
94+
其中 :math:`T` 为时窗长度。虚频率的加入其中一个目的就是使核函数的极点略微偏离实轴,
95+
从而可以进行常规的波数积分。但当震中距增大,模型速度减小,计算时就需要更宽的时窗来容纳所有有效信号以避免混叠,
96+
代价是虚频率减小,核函数的极点逐渐靠近实轴,导致核函数计算的不稳定性风险增加。
97+
98+
核函数有多个极点,且随频率变化,而从实际计算来看,虚频率的减小对极低频的核函数计算影响很大。
99+
这是因为在核函数的计算中,无时延的广义 R/T 系数矩阵仅和相速度有关,当引入虚频率后,对应的复数形式的相速度定义为
100+
:math:`\tilde{c}=\dfrac{\omega - j \omega_I}{k}` ,当实频率 :math:`\omega` 和虚频率 :math:`\omega_I`
101+
都很小时, :math:`\tilde{c} \rightarrow 0` ,这就导致核函数计算不稳定,也就导致低频结果有偏差,整体波形在漂。
102+
103+
解决方法
104+
-----------------
105+
既然这是方法本身的局限性导致这些低频的几个点计算不出精确结果,不如直接放弃它们。
106+
107+
在以上计算格林函数时,可以看到为了复现上述问题,我添加了一些参数 ( ``-N+a`` 和 ``keepAllFreq`` ),
108+
这是因为从 0.13.0 版本起, **程序默认会根据复频率的大小跳过低频的计算。**
109+
具体而言,程序不计算 :math:`|\omega - \omega_I| < 0.1` 的频谱,并会在终端打印对应的Warning。
110+
这本质就是自动进行了高通滤波。
111+
如果觉得阈值不合适或需要进行数值实验,可加上上述参数以计算所有频率,然后再手动设置频段。
112+
113+
以下是跳过这些频段后的波形(计算时不使用 ``-N+a`` 和 ``keepAllFreq`` )。
114+
115+
.. figure:: run/grn3.png
116+
:align: center
117+
:width: 100%

docs/source/Module/greenfn.rst

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
:author: 朱邓达
22
:date: 2025-09-22
3-
:updated_date: 2025-11-24
3+
:updated_date: 2025-12-05
44

55
.. include:: common_OPTs.rst_
66

@@ -16,7 +16,7 @@ greenfn
1616
**grt greenfn**
1717
|-M|\ *model*
1818
|-D|\ *depsrc/deprcv*
19-
|-N|\ *nt/dt*\ [**+w**\ *zeta*][**+n**\ *fac*]
19+
|-N|\ *nt/dt*\ [**+w**\ *zeta*][**+n**\ *fac*][**+a**]
2020
|-R|\ *file*\|\ *r1,r2,...*
2121
|-O|\ *outdir*
2222
[ |-H|\ *f1/f2* ]
@@ -56,7 +56,7 @@ greenfn
5656

5757
.. _-N:
5858

59-
**-N**\ *nt/dt*\ [**+w**\ *zeta*][**+n**\ *fac*]
59+
**-N**\ *nt/dt*\ [**+w**\ *zeta*][**+n**\ *fac*][**+a**]
6060
采样点数 *nt* 和采样时间间隔 *dt* (secs) ,这将决定计算的最高频。还可设置:
6161

6262
+ **+w**\ *zeta* - 虚频率系数 :math:`\zeta` [0.8]。Bouchon (1981) 提出在频率上添加一个虚频率偏移,
@@ -66,6 +66,10 @@ greenfn
6666
+ **+n**\ *fac* - 频率域插值倍数[1]。即在做逆傅里叶变换时,在频域上最高频后补零,
6767
相当于 *nt* ← *nt* \* *fac* , *dt* ← *dt* / *fac* ,使计算的波形更平滑。
6868

69+
+ **+a** - 计算所有频点,不论频率多低。除非做数值实验,否则不建议使用该选项。
70+
默认情况下,程序会跳过非常低频的几个点以避免引入误差,
71+
详见 :doc:`/Advanced/waveform_drift/waveform_drift` 的介绍。
72+
6973
当时窗长度 nt\*dt 太小“包不住”有效信号,或时窗长度足够但时延(|-E|)不合适,输出的波形会发生混叠,
7074
此时需调整 |-N| 和 |-E| 。
7175

docs/source/run_all.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,10 @@ if [[ $1 == '8' || $1 == '' ]]; then
6969
python lamb1_plot_time.py
7070
python lamb1_plot_freq_time.py
7171
cd -
72+
fi
73+
74+
if [[ $1 == '9' || $1 == '' ]]; then
75+
cd Advanced/waveform_drift/run
76+
python run.py
77+
cd -
7278
fi

pygrt/C_extension/src/dynamic/grt_greenfn.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ printf("\n"
281281
"Usage:\n"
282282
"----------------------------------------------------------------\n"
283283
" grt greenfn -M<model> -D<depsrc>/<deprcv> \n"
284-
" -N<nt>/<dt>[+w<zeta>][+n<fac>] \n"
284+
" -N<nt>/<dt>[+w<zeta>][+n<fac>][+a] \n"
285285
" -R<r1>,<r2>[,...] -O<outdir> [-H<f1>/<f2>] \n"
286286
" [-L<length>] [-E<t0>[/<v0>]] \n"
287287
" [-K[+k<k0>][+s<ampk>][+e<keps>][+v<vmin>]]\n"

0 commit comments

Comments
 (0)