Wavelets
sim2x.Wavelet
A class that encapsulates wavelets in sim2x
Attributes:
Name | Type | Description |
---|---|---|
nsamp |
Number of samples |
|
dt |
Sample rate |
|
units |
time axis units ("s", "ms") |
|
df |
Spectral sample rate |
|
time |
Time array |
|
amp |
Amp array |
|
name |
Name or identifier |
|
wtype |
Wavelet Type |
famp: ndarray
property
readonly
the spectra amplitude
fmag: ndarray
property
readonly
the spectra magnitude
fphase: ndarray
property
readonly
the spectra phase
freq: ndarray
property
readonly
the spectra frequency
spectra: ndarray
property
readonly
the wavelet spectra
__init__(self, nsamp=128, dt=0.002, df=10, positive_spectra=True, name=None, units='s')
special
Initialise a wavelet class
Parameters:
Name | Type | Description | Default |
---|---|---|---|
nsamp |
int |
Number of samples in the wavelet |
128 |
dt |
float |
Sample rate of the wavelet (seconds) |
0.002 |
df |
float |
The sample rate in the frequency domain |
10 |
positive_spectra |
bool |
Return only the positive portion of the frequency spectrum |
True |
name |
Union[NoneType, str] |
Name or descriptor of the wavelet |
None |
units |
Literal['s', 'ms'] |
The units of the time axis, either s or ms |
's' |
as_milliseconds(self)
Convert time axis to milliseconds
as_seconds(self)
Convert time axis to seconds
resample(self, dt)
Resample the wavelet to a new dt
Parameters:
Name | Type | Description | Default |
---|---|---|---|
dt |
float |
new sample rate |
required |
set_wavelet(self, time=None, amp=None)
Set the wavelet manually
time
and amp
are 1D arrays with the same size.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
time |
Union[NoneType, numpy.ndarray] |
The time array |
None |
amp |
Union[NoneType, numpy.ndarray] |
The amplitude array |
None |
shift_phase(self, shift)
Rotate the phase of the wavelet.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
shift |
float |
Phase shift in degrees |
required |
sim2x.RickerWavelet
Ricker wavelet class
Extends the base Wavelet
class.
Attributes:
Name | Type | Description |
---|---|---|
f |
float |
dominant frequency in Hz |
__init__(self, f, **kwargs)
special
Initialise a wavelet class
Parameters:
Name | Type | Description | Default |
---|---|---|---|
f |
float |
The dominant frequency of the wavelet |
required |
kwargs |
keyword arguments passed to |
{} |
sim2x.BandpassWavelet
A bandpass wavelet
Extends the base Wavelet
class.
Attributes:
Name | Type | Description |
---|---|---|
fbounds |
tuple |
The bandpass boundaries tuple |
__init__(self, fbounds, norm=True, precision=0.1, **kwargs)
special
Initialise a wavelet class
Parameters:
Name | Type | Description | Default |
---|---|---|---|
fbounds |
Tuple[float, float, float, float] |
The bandpass frequency boundaries tuple |
required |
norm |
bool |
True |
|
precision |
float |
0.1 |
|
kwargs |
keyword arguments passed to |
{} |
sim2x.OrmsbyWavelet
An Ormsby wavelet
Extends the base Wavelet
class.
Attributes:
Name | Type | Description |
---|---|---|
fbounds |
tuple |
The bandpass boundaries tuple |
__init__(self, fbounds, norm=True, **kwargs)
special
Initialise a wavelet class
Parameters:
Name | Type | Description | Default |
---|---|---|---|
fbounds |
Tuple[float, float, float, float] |
The Ormsby frequency boundaries tuple |
required |
norm |
bool |
True |
|
kwargs |
keyword arguments passed to |
{} |
sim2x.PetrelWavelet
A wavelet loaded from a Petrel file.
Extends the base Wavelet
class.
Attributes:
Name | Type | Description |
---|---|---|
file |
str |
The filepath of the loaded wavelet |
__init__(self, filepath, norm=False)
special
Load a Petrel format exported wavelet to this class.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
filepath |
Union[str, pathlib.Path] |
The full filepath and name to the exported ascii wavelet file. |
required |
Wavelet Functions
Functions and classes necessary for handling wavelets.
bandpass(nsamp, dt, f1, f2, f3, f4, norm=True, precision=0.1)
Create a bandpass wavelet
Parameters:
Name | Type | Description | Default |
---|---|---|---|
nsamp |
int |
Number of samples in wavelet |
required |
dt |
float |
Sample rate in seconds |
required |
f1 |
float |
Lower frequency off |
required |
f2 |
float |
Lower frequency on |
required |
f3 |
float |
Upper frequency on |
required |
f4 |
float |
Upper frequency off |
required |
norm |
bool |
Normalise the output |
True |
precision |
float |
in frequency domain (1/Hz) |
0.1 |
Returns:
Type | Description |
---|---|
ndarray |
Structured array of length nsamp+1 tuples ('time', 'amp') |
Notes
Bandpass amplitude spectrum.
1 f2________f3
/ \
/ \
0 _____/ \______
f1 f4
Source code in sim2x/_sim2seis/_wavelets.py
def bandpass(
nsamp: int,
dt: float,
f1: float,
f2: float,
f3: float,
f4: float,
norm: bool = True,
precision: float = 0.1,
) -> WaveletDtype:
"""Create a bandpass wavelet
Args:
nsamp: Number of samples in wavelet
dt: Sample rate in seconds
f1: Lower frequency off
f2: Lower frequency on
f3: Upper frequency on
f4: Upper frequency off
norm: Normalise the output
precision: in frequency domain (1/Hz)
Returns:
Structured array of length nsamp+1 tuples ('time', 'amp')
Notes:
Bandpass amplitude spectrum.
```
1 f2________f3
/ \\
/ \\
0 _____/ \\______
f1 f4
```
"""
time = zero_phase_time_axis(nsamp, dt)
# frequency domain spectrum and filtering
precision = int(1 / precision)
spec_samp = nsamp * precision
spec_time = np.linspace(-spec_samp, spec_samp, 2 * spec_samp + 1) * dt
spec_freq = np.fft.fftfreq(2 * spec_samp + 1, dt)
abs_spec_freq = np.abs(spec_freq)
nf = spec_freq.shape[0]
hnf = int((nf - 1) / 2)
spec_amp = np.ones_like(spec_freq)
blank = np.logical_or(abs_spec_freq <= f1, abs_spec_freq >= f4)
spec_amp = np.where(blank, 0, spec_amp)
rampup = np.logical_and(abs_spec_freq > f1, abs_spec_freq < f2)
m1 = 1.0 / (f2 - f1)
c1 = -m1 * f1
spec_amp = np.where(rampup, m1 * abs_spec_freq + c1, spec_amp)
rampdown = np.logical_and(abs_spec_freq > f3, abs_spec_freq < f4)
m2 = 1.0 / (f3 - f4)
c2 = -m2 * f4
spec_amp = np.where(rampdown, m2 * abs_spec_freq + c2, spec_amp)
# inverse fft and unwrap
time_amp = np.fft.ifft(spec_amp)
time_amp = np.r_[time_amp[hnf:], time_amp[:hnf]]
# interpolation to out
amp_func = interp1d(spec_time[:-1], time_amp.real[1:], kind="cubic")
amp = amp_func(time)
if norm:
amp = amp / np.max(amp)
return np.array([zipped for zipped in zip(time, amp)], dtype=wavelet_dtype)
gabor(nsamp, dt, a, k0)
Gabor wavelet - Gausian modulated by an exponential
Parameters:
Name | Type | Description | Default |
---|---|---|---|
nsamp |
int |
Number of samples in the wavelet |
required |
dt |
float |
Sample rate of the wavelet (s) |
required |
a |
float |
rate of exponential attenuation away from t=0 |
required |
k0 |
float |
rate of modulation |
required |
Returns:
Type | Description |
---|---|
ndarray |
Structured array of length nsamp+1 tuples ('time', 'amp') |
Source code in sim2x/_sim2seis/_wavelets.py
def gabor(nsamp: int, dt: float, a: float, k0: float) -> WaveletDtype:
"""Gabor wavelet - Gausian modulated by an exponential
Args:
nsamp: Number of samples in the wavelet
dt: Sample rate of the wavelet (s)
a: rate of exponential attenuation away from t=0
k0: rate of modulation
Returns:
Structured array of length nsamp+1 tuples ('time', 'amp')
"""
time = zero_phase_time_axis(nsamp, dt)
i = complex(0, 1)
amp = np.real(np.exp(-1 * np.square(time) / (a * a)) * np.exp(-i * k0 * time))
return np.array([zipped for zipped in zip(time, amp)], dtype=wavelet_dtype)
ormsby(nsamp, dt, f1, f2, f3, f4, norm=True)
Create an Ormsby filtered wavelet
Parameters:
Name | Type | Description | Default |
---|---|---|---|
nsamp |
int |
Number of samples in wavelet |
required |
dt |
float |
Sample rate in (s) |
required |
f1 |
float |
Low frequency cut off (Hz) |
required |
f2 |
float |
Low frequency pass (Hz) |
required |
f3 |
float |
High frequency pass (Hz) |
required |
f4 |
float |
High freuency cut off (Hz) |
required |
norm |
bool |
Normalise the wavelet |
True |
Returns:
Type | Description |
---|---|
ndarray |
Structured array of length nsamp+1 tuples ('time', 'amp') |
Source code in sim2x/_sim2seis/_wavelets.py
def ormsby(
nsamp: int, dt: float, f1: float, f2: float, f3: float, f4: float, norm: bool = True
) -> WaveletDtype:
"""Create an Ormsby filtered wavelet
Args:
nsamp: Number of samples in wavelet
dt: Sample rate in (s)
f1: Low frequency cut off (Hz)
f2: Low frequency pass (Hz)
f3: High frequency pass (Hz)
f4: High freuency cut off (Hz)
norm: Normalise the wavelet
Returns:
Structured array of length nsamp+1 tuples ('time', 'amp')
"""
time = zero_phase_time_axis(nsamp, dt)
pi = np.pi
c1 = np.square(pi * f4) / (pi * f4 - pi * f3)
c2 = np.square(pi * f3) / (pi * f4 - pi * f3)
c3 = np.square(pi * f2) / (pi * f2 - pi * f1)
c4 = np.square(pi * f1) / (pi * f2 - pi * f1)
amp = (
c1 * np.square(np.sinc(pi * f4 * time))
- c2 * np.square(np.sinc(pi * f3 * time))
- c3 * np.square(np.sinc(pi * f2 * time))
+ c4 * np.square(np.sinc(pi * f1 * time))
)
if norm:
amp = amp / np.max(amp)
return np.array([zipped for zipped in zip(time, amp)], dtype=wavelet_dtype)
ricker(nsamp, dt, f)
Create a Ricker Wavelet
Create an analytical Ricker wavelet with a dominant frequency of f
.
Length in samples, dt
im mSec, f
in Hertz
Parameters:
Name | Type | Description | Default |
---|---|---|---|
nsamp |
int |
Number of samples in wavelet |
required |
dt |
float |
Sample rate in (s) |
required |
f |
float |
Dominant Frequency of the wavelet in Hz |
required |
Returns:
Type | Description |
---|---|
ndarray |
Structured array of length nsamp+1 tuples ('time', 'amp') |
Source code in sim2x/_sim2seis/_wavelets.py
def ricker(nsamp: int, dt: float, f: float) -> WaveletDtype:
"""Create a Ricker Wavelet
Create an analytical Ricker wavelet with a dominant frequency of `f`.
Length in samples, `dt` im mSec, `f` in Hertz
Args:
nsamp: Number of samples in wavelet
dt: Sample rate in (s)
f: Dominant Frequency of the wavelet in Hz
Returns:
Structured array of length nsamp+1 tuples ('time', 'amp')
"""
time = zero_phase_time_axis(nsamp, dt)
amp = (1 - 2 * np.square(np.pi * f * time)) * np.exp(-np.square(np.pi * f * time))
return np.array([zipped for zipped in zip(time, amp)], dtype=wavelet_dtype)
wavelet_spectra(dt, amp, df=10)
Calculate the spectra of the wavelet
Performs a padded FFT of the wavelet to calculate the various spectral components of the complex signal.
freq
: Frequency (Hz) at which the spectra has been evaluatedamp
: The complex amplitude after the forwart FFTmag
:np.abs(amp)
- magnitude ofamp
phase
: Phase angle of the complex amplitude signal
Parameters:
Name | Type | Description | Default |
---|---|---|---|
dt |
float |
The wavelet sample interval (s) |
required |
amp |
ndarray |
The wavelet amplitude array |
required |
df |
float |
Desired frequency sample rate Hz |
10 |
Returns:
Type | Description |
---|---|
ndarray |
Structured array ('freq', 'amp', 'mag', 'phase') |
Notes
DFTs are vulnerable to spectral leakage. This occurs when a non-interger number of periods exist within a signal. Spectral leakage allows a single-tone signal disperse across several frequencies after the DFT operation.
Zero-padding a signal does not reveal more information about the spectrum, but it only interpolates between the frequency bins that would occur when no zero-padding is applied, i.e. zero-padding does not increase spectral resolution. More measured signal is required for this.
Source code in sim2x/_sim2seis/_wavelets.py
def wavelet_spectra(dt: float, amp: npt.NDArray[Any], df: float = 10) -> SpectraDtype:
"""Calculate the spectra of the wavelet
Performs a padded FFT of the wavelet to calculate the various
spectral components of the complex signal.
- `freq`: Frequency (Hz) at which the spectra has been evaluated
- `amp`: The complex amplitude after the forwart FFT
- `mag`: `np.abs(amp)` - magnitude of `amp`
- `phase`: Phase angle of the complex amplitude signal
Args:
dt: The wavelet sample interval (s)
amp: The wavelet amplitude array
df: Desired frequency sample rate Hz
Returns:
Structured array ('freq', 'amp', 'mag', 'phase')
Notes:
DFTs are vulnerable to spectral leakage. This occurs when a non-interger
number of periods exist within a signal. Spectral leakage allows a single-tone
signal disperse across several frequencies after the DFT operation.
Zero-padding a signal does not reveal more information about the spectrum, but it only
interpolates between the frequency bins that would occur when no zero-padding is
applied, i.e. zero-padding does not increase spectral resolution. More measured signal
is required for this.
"""
assert len(amp.shape) == 1
nsamp = amp.size
# increase the number of samples to get the desired spectral sampling
_df = 1 / (nsamp * dt)
if df < _df:
psamp = nsamp * int(round(_df / df))
else:
psamp = nsamp
famp = fftpack.fftshift(fftpack.fft(amp, n=psamp))
freq = fftpack.fftshift(fftpack.fftfreq(psamp, dt))
fmag = np.abs(famp)
phase = np.angle(famp)
phase[fmag < 1] = 0
return np.array(
[zipped for zipped in zip(freq, famp, fmag, phase)],
dtype=spectra_dtype,
)