你已经派生过 gpr3dattribute
镜像自地址
https://gitee.com/sduem/gpr3dattribute.git
已同步 2025-09-19 01:13:49 +08:00
431 行
14 KiB
Python
431 行
14 KiB
Python
#! /usr/bin/env python
|
|
#
|
|
# Implements an FK (Stolt) migration routine.
|
|
#
|
|
# Dmig, tmig, xmig = fkmig(D, dt, dx, v, params)
|
|
#
|
|
# D data array
|
|
# dt temporal sample rate
|
|
# dx spatial sample rate
|
|
# v constant migration velocity
|
|
# params migration parameters (not yet implemented)
|
|
#
|
|
# Code translated from CREWES MATLAB algorithm, and similar restrictions
|
|
# presumably apply. The original license terms are included below.
|
|
#
|
|
# BEGIN TERMS OF USE LICENSE
|
|
#
|
|
# This SOFTWARE is maintained by the CREWES Project at the Department
|
|
# of Geology and Geophysics of the University of Calgary, Calgary,
|
|
# Alberta, Canada. The copyright and ownership is jointly held by
|
|
# its author (identified above) and the CREWES Project. The CREWES
|
|
# project may be contacted via email at: crewesinfo@crewes.org
|
|
#
|
|
# The term 'SOFTWARE' refers to the Matlab source code, translations to
|
|
# any other computer language, or object code
|
|
#
|
|
# Terms of use of this SOFTWARE
|
|
#
|
|
# 1) Use of this SOFTWARE by any for-profit commercial organization is
|
|
# expressly forbidden unless said organization is a CREWES Project
|
|
# Sponsor.
|
|
#
|
|
# 2) A CREWES Project sponsor may use this SOFTWARE under the terms of the
|
|
# CREWES Project Sponsorship agreement.
|
|
#
|
|
# 3) A student or employee of a non-profit educational institution may
|
|
# use this SOFTWARE subject to the following terms and conditions:
|
|
# - this SOFTWARE is for teaching or research purposes only.
|
|
# - this SOFTWARE may be distributed to other students or researchers
|
|
# provided that these license terms are included.
|
|
# - reselling the SOFTWARE, or including it or any portion of it, in any
|
|
# software that will be resold is expressly forbidden.
|
|
# - transfering the SOFTWARE in any form to a commercial firm or any
|
|
# other for-profit organization is expressly forbidden.
|
|
#
|
|
#
|
|
|
|
#from __future__ import print_function
|
|
import math
|
|
import numpy as np
|
|
import pdb, traceback
|
|
from tqdm import tqdm
|
|
|
|
def csinci():
|
|
""" Complex valued sinc function interpolation.
|
|
|
|
trout = csinci(trin, t, tout, sizetable)
|
|
"""
|
|
|
|
def fktran(D, t, x, ntpad=None, nxpad=None, percent=0., ishift=1):
|
|
""" F-K transform using fft on time domain and ifft on space domain. """
|
|
nsamp = D.shape[0]#时间采集点数
|
|
ntr = D.shape[1]#x距离采集点数
|
|
|
|
if len(t) != nsamp:
|
|
raise Exception('Time domain length is inconsistent in input')
|
|
if len(x) != ntr:
|
|
raise Exception('Space domain length is inconsistent in input')
|
|
|
|
if ntpad is None:
|
|
ntpad = 2**nextpow2(t) #""" Gives the next power of 2 larger than a. """return np.ceil(np.log(a) / np.log(2)).astype(int)
|
|
if nxpad is None:
|
|
nxpad = 2**nextpow2(x)
|
|
|
|
# Get real values of transform with fftrl
|
|
specfx, f = fftrl(D, t, percent, ntpad)###傅里叶变换
|
|
|
|
# Taper and pad in space domain
|
|
if percent > 0.:
|
|
mw = np.tile(mwindow(ntr, percent), (ntr, 1))
|
|
specfx = specfx * mw
|
|
if ntr < nxpad:
|
|
ntr = nxpad # this causes ifft to apply the x padding
|
|
|
|
spec = np.fft.ifft(specfx.T, n=ntr, axis=0).T
|
|
# Compute kx
|
|
kxnyq = 1. / (2. * (x[1] - x[0]))
|
|
dkx = 2. * kxnyq / ntr
|
|
kx = np.hstack([np.arange(0, kxnyq, dkx), np.arange(-kxnyq, 0, dkx)])
|
|
|
|
if ishift:
|
|
tmp = zip(kx, spec)
|
|
tmp.sort()
|
|
kx = [i[0] for i in tmp]
|
|
spec = [i[1] for i in tmp]
|
|
return spec, f, kx
|
|
|
|
|
|
def fftrl(s, t, percent=0.0, n=None):
|
|
""" Returns the real part of the forward Fourier transform. """
|
|
# Determine the number of traces in ensemble
|
|
l = s.shape[0]
|
|
m = s.shape[1]
|
|
ntraces = 1
|
|
itr = 0 # transpose flag
|
|
if l == 1:
|
|
nsamps = m
|
|
itr = 1
|
|
s = s.T
|
|
elif m == 1:
|
|
nsamps = l
|
|
else:
|
|
nsamps = l
|
|
ntraces = m
|
|
if nsamps != len(t):
|
|
t = t[0] + (t[1] - t[0]) * np.arange(0, nsamps)
|
|
if n is None:
|
|
n = len(t)
|
|
|
|
# Apply the taper
|
|
if percent > 0.0:
|
|
mw = np.tile(mwindow(nsamps, percent), (ntraces, 1))
|
|
s = s * mw
|
|
# Pad s if needed
|
|
if nsamps < n:
|
|
s = np.vstack([s, np.zeros([n-nsamps, ntraces])])
|
|
nsamps = n
|
|
|
|
# Do the transformation
|
|
spec = np.fft.fft(s, n=nsamps, axis=0)
|
|
spec = spec[:int(n/2)+1, :] # save only positive frequencies
|
|
|
|
# Build the frequency vector
|
|
fnyq = 1. / (2 * (t[1] - t[0]))
|
|
nf = spec.shape[0]
|
|
df = 2.0 * fnyq / n
|
|
f = df * np.arange(0,nf).T
|
|
if itr:
|
|
f = f.T
|
|
spec = spec.T
|
|
return spec, f
|
|
|
|
def ifktran(spec, f, kx, nfpad=None, nkpad=None, percent=0.0):
|
|
""" Inverse f-k transform.
|
|
Arguments:
|
|
spec complex valued f-k series
|
|
f frequency components for rows of spec
|
|
kx wavenumber components for columns of spec
|
|
nfpad size to pad spec rows to
|
|
nkpad size to pad spec columns to
|
|
percent controls cosine taper
|
|
|
|
Returns:
|
|
D 2-d array; one trace per column
|
|
t time coordinates for D
|
|
x space coordinates for D
|
|
"""
|
|
nf,nkx = spec.shape
|
|
|
|
if len(f) != nf:
|
|
raise Exception('frequency coordinate vector is wrong size')
|
|
elif len(kx) != nkx:
|
|
raise Exception('wavenumber coordinate vector is wrong size')
|
|
|
|
if nfpad is None:
|
|
nfpad = 2**nextpow2(len(f))
|
|
if nkpad is None:
|
|
nkpad = 2**nextpow2(len(kx))
|
|
|
|
# Determine if kx needs to be wrapped
|
|
if kx[0] < 0.0:
|
|
# Looks unwrapped (is this wise?)
|
|
ind = kx >= 0.0
|
|
kx = np.hstack([kx[ind], kx[np.arange(ind[0])]])
|
|
spec = np.hstack([spec[:,ind], spec[:,np.arange(ind[0])]])
|
|
else:
|
|
ind = False
|
|
|
|
# Taper and pad in kx
|
|
if percent > 0.0:
|
|
mw = mwindow(nkx, percent)
|
|
if ind.any():
|
|
mw = np.hstack([mw[ind], mw[np.arange(ind[0])]])
|
|
mw = mw.repeat(nkz, axis=0)
|
|
spec = spec * mw
|
|
if nkx < nkpad:
|
|
nkx = nkpad
|
|
|
|
# Performs the transforms
|
|
specfx = np.fft.fft(spec, nkx)
|
|
D, t = ifftrl(specfx, f)
|
|
|
|
# Compute x
|
|
dkx = kx[1] - kx[0]
|
|
xmax = 1.0 / dkx
|
|
dx = xmax / nkx
|
|
x = np.arange(0, xmax, dx)
|
|
return D, t, x
|
|
|
|
def ifftrl(spec, f):
|
|
""" Inverse Fourier transform for real-valued series.
|
|
Arguments:
|
|
spec input spectrum
|
|
f input frequency coordinates
|
|
Returns:
|
|
r output trace
|
|
t output time vector
|
|
"""
|
|
m,n = spec.shape # Will be a problem if spec is 1-dimensional
|
|
itr = 0
|
|
if (m == 1) or (n == 1):
|
|
if m == 1:
|
|
spec = spec.T
|
|
itr = 1
|
|
nsamp = len(spec)
|
|
ntr = 1
|
|
else:
|
|
nsamp = m
|
|
ntr = n
|
|
|
|
# Form the conjugate symmetric complex spectrum expected by ifft
|
|
# Test for nyquist
|
|
nyq = 0
|
|
if (spec[-1] == np.real(spec[-1])).all():
|
|
nyq = 1
|
|
if nyq:
|
|
L1 = np.arange(nsamp)
|
|
L2 = L1[-2:0:-1]
|
|
else:
|
|
L1 = np.arange(nsamp)
|
|
L2 = L1[-2:0:-1] # WTF? -njw
|
|
symspec = np.vstack([spec[L1,:], np.conj(spec[L2,:])])
|
|
# Transform the array
|
|
r = (np.fft.ifft(symspec.T)).real.T
|
|
# Build the time vector
|
|
n = len(r)
|
|
df = f[1] - f[0]
|
|
dt = 1.0 / (n*df)
|
|
t = dt * np.arange(n).T
|
|
if itr == 1:
|
|
r = r.T
|
|
t = t.T
|
|
return r, t
|
|
|
|
def mwindow(n, percent=10.):
|
|
""" Creates a boxcar window with raised-cosine tapers. """
|
|
if type(n) is not int and type(n) is not float:
|
|
n = len(n)
|
|
# Compute the hanning function
|
|
if percent > 50. or percent < 0.:
|
|
raise Exception('Invalid percent in function mwindow (={0})'.format(percent))
|
|
m = 2.0 * math.floor(percent * n / 100.)
|
|
h = np.hanning(m)
|
|
return np.hstack([h[:m/2], np.ones([n-m]), h[m/2:]])
|
|
|
|
def mwhalf(n, percent=10.):
|
|
""" Half mwindow. """
|
|
if type(n) is not int and type(n) is not float:
|
|
n = len(n)
|
|
# Compute the hanning function
|
|
if percent > 100. or percent < 0.:
|
|
raise Exception('Invalid percent in function mwhalf (={0})'.format(percent))
|
|
m = int(math.floor(percent * n / 100.))
|
|
h = np.hanning(2*m)
|
|
return np.hstack([np.ones([n-m]), h[m:0:-1]])
|
|
|
|
def nextpow2(a):
|
|
""" Gives the next power of 2 larger than a. """
|
|
return np.ceil(np.log(a) / np.log(2)).astype(int)
|
|
|
|
def fkmig(D, dt, dx, v, params=None):
|
|
|
|
nsamp = D.shape[0]##时间采集点数
|
|
ntr = D.shape[1]#x采集点数
|
|
t = np.arange(0, nsamp) * dt#生成时间linspace
|
|
x = np.arange(0, ntr) * dx#生成距离linspace
|
|
interpolated = True
|
|
|
|
fnyq = 1.0 / (2.0*dt)#采样频率,存疑,为什么是2
|
|
knyq = 1.0 / (2.0*dx)#
|
|
tmax = t[-1]#最大时间
|
|
xmax = abs(x[-1]-x[0])#最大距离
|
|
|
|
# Deal with parameters#
|
|
if params == None:
|
|
fmax = 0.6 * fnyq
|
|
fwid = 0.2 * (fnyq - fmax)#中频?
|
|
dipmax = 85.0
|
|
dipwid = 90.0 - dipmax#5
|
|
tpad = min([0.5 * tmax, abs(tmax / math.cos(math.pi*dipmax / 180.0))])
|
|
xpad = min([0.5 * xmax, xmax / math.sin(math.pi*dipmax / 180.0)])
|
|
padflag = 1
|
|
intflag = 3
|
|
cosflag = 1
|
|
lsinc = 1
|
|
ntable = 25
|
|
mcflag = 0 # Faster, less memory-efficient transform (not implemented)
|
|
kpflag = 50.0
|
|
|
|
# Apply padding#填充0值,优化傅里叶变换的性能
|
|
# tpad
|
|
nsampnew = int(2.0**nextpow2( round((tmax+tpad) / dt + 1.0) ))# """ Gives the next power of 2 larger than a. return np.ceil(np.log(a) / np.log(2)).astype(int)
|
|
tmaxnew = (nsampnew-1)*dt
|
|
tnew = np.arange(t[0], tmaxnew+dt, dt)#新的采样linespace
|
|
ntpad = nsampnew-nsamp
|
|
D = np.vstack([D,np.zeros([ntpad,ntr])])
|
|
|
|
# xpad
|
|
ntrnew = 2**nextpow2( round((xmax+xpad) / dx + 1) )#
|
|
xmaxnew = (ntrnew-1)*dx + x[0]
|
|
xnew = np.arange(x[0], xmaxnew+dx, dx)#新的采样linespace
|
|
nxpad = ntrnew - ntr
|
|
D = np.hstack([D, np.zeros([nsampnew,nxpad])])
|
|
|
|
|
|
|
|
# Forward f-k transform
|
|
fkspec, f, kx = fktran(D, tnew, xnew, nsampnew, ntrnew, 0, 0)
|
|
df = f[1] - f[0]
|
|
nf = len(f)
|
|
|
|
# Compute frequency mask
|
|
ifmaxmig = int(round((fmax+fwid) / df + 1.0))
|
|
pct = 100.0 * (fwid / (fmax+fwid))
|
|
fmask = np.hstack([mwhalf(ifmaxmig,pct), np.zeros([nf-ifmaxmig])])
|
|
fmaxmig = (ifmaxmig-1)*df # i.e. fmax+fwid to nearest sample
|
|
|
|
# Now loop over wavenumbers
|
|
ve = v / 2.0 # exploding reflector velocity
|
|
dkz = df / ve
|
|
kz = (np.arange(0,len(f)) * dkz).T
|
|
kz2 = kz ** 2
|
|
|
|
th1 = dipmax * math.pi / 180.0
|
|
th2 = (dipmax+dipwid) * math.pi / 180.0
|
|
|
|
if th1 == th2:
|
|
print("No dip filtering")
|
|
|
|
for j,kxi in enumerate(kx):
|
|
# Evanescent cut-off
|
|
fmin = abs(kxi) * ve
|
|
ifmin = int(math.ceil(fmin / df)) + 1
|
|
|
|
# Compute dip mask
|
|
if th1 != th2:
|
|
# First physical frequency excluding dc
|
|
ifbeg = max([ifmin, 1])+1
|
|
# Frequencies to migrate
|
|
ifuse = np.arange(ifbeg, ifmaxmig+1)
|
|
# if len(ifuse) == 1: # This was before 4/18/2020
|
|
if len(ifuse) <= 1: # Alain changed this on 4/18/2020
|
|
# Special case
|
|
dipmask = np.zeros(f.shape)
|
|
dipmask[ifuse-1] = 1
|
|
else:
|
|
# Physical dips for each frequency
|
|
theta = np.arcsin(fmin / f[ifuse])
|
|
# Sample number to begin ramp
|
|
if1 = int(round(fmin / (math.sin(th1) * df)))
|
|
if1 = max([if1, ifbeg])
|
|
# sample number to end ramp
|
|
if2 = int(round(fmin / (math.sin(th2) * df)))
|
|
if2 = max([if2, ifbeg])
|
|
# Initialize mask to zeros
|
|
dipmask = np.zeros(f.shape)
|
|
# Pass these dips
|
|
dipmask[if1:nf-1] = 1
|
|
dipmask[if2:if1] = 0.5 + 0.5 * np.cos(
|
|
(theta[np.arange(if2, if1) - ifbeg] - th1)
|
|
* math.pi / float(th2-th1))
|
|
else:
|
|
dipmask = np.ones(f.shape)
|
|
|
|
# Apply masks
|
|
tmp = fkspec[:, j] * fmask * dipmask
|
|
|
|
# Compute f that map to kz
|
|
fmap = ve * np.sqrt(kx[j]**2 + kz2)
|
|
# Contains one value for each kz giving the frequency
|
|
# that maps there to migrate the data
|
|
# Many of these frequencies will be far too high
|
|
ind = np.vstack(np.nonzero(fmap <= fmaxmig)).T
|
|
# ind is an array of indicies of fmap which will always start at 1
|
|
# and end at the highest f to be migrated
|
|
|
|
# Now map samples by interpolation
|
|
fkspec[:, j] *= 0.0 # initialize output spectrum to zero
|
|
if len(ind) != 0:
|
|
# Compute cosine scale factor
|
|
if cosflag:
|
|
if fmap[ind].all() == 0:
|
|
scl = np.ones(ind.shape[0])
|
|
li = ind.shape[0]
|
|
scl[1:li] = (ve * kz[ind[1:li]] / fmap[ind[1:li]])[:,0]
|
|
else:
|
|
scl = ve * kz[ind] / fmap[ind]
|
|
else:
|
|
scl = np.ones(ind.shape[0])
|
|
if intflag == 0:
|
|
# Nearest neighbour interpolation
|
|
ifmap = (fmap[ind] / df).astype(int)
|
|
fkspec[ind, j] = (scl.squeeze() \
|
|
* tmp[ifmap.squeeze()]).reshape([-1,1])
|
|
elif intflag == 1:
|
|
# Complex sinc interpolation
|
|
fkspec[ind, j] = scl \
|
|
* csinci(tmp, f, fmap[ind], np.hstack([lsinc,ntable]))
|
|
elif intflag == 2:
|
|
# Spline interpolation
|
|
# Not implemented
|
|
pass
|
|
elif intflag == 3:
|
|
# Linear interpolation
|
|
r_interp = scl.squeeze() \
|
|
* np.interp(fmap[ind], f, tmp.real).squeeze()
|
|
j_interp = scl.squeeze() \
|
|
* np.interp(fmap[ind], f, tmp.imag).squeeze()
|
|
fkspec[ind, j] = (r_interp + j_interp * 1j).reshape([-1,1])
|
|
|
|
# Inverse transform
|
|
Dmig, tmig, xmig = ifktran(fkspec, f, kx)
|
|
|
|
# Remove padding, if desired
|
|
if padflag:
|
|
Dmig = Dmig[:nsamp, :ntr]
|
|
tmig = tmig[:nsamp]
|
|
xmig = xmig[:ntr]
|
|
|
|
return Dmig, tmig, xmig
|