Skip to content

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 Wavelet

{}

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 Wavelet

{}

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 Wavelet

{}

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.

Text Only
1         f2________f3
          /          \
         /            \
0  _____/              \______
       f1               f4
Source code in sim2x/_sim2seis/_wavelets.py
Python
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
Python
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
Python
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
Python
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 evaluated
  • amp: The complex amplitude after the forwart FFT
  • mag: np.abs(amp) - magnitude of amp
  • 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
Python
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,
    )