Beispiel #1
0
def test_taper3d(par):
    """Create taper wavelet and check size and values"""
    tap = taper3d(par["nt"], par["nspat"], par["ntap"], par["tapertype"])

    assert tap.shape == (par["nspat"][0], par["nspat"][1], par["nt"])
    assert_array_equal(tap[0][0], np.zeros(par["nt"]))
    assert_array_equal(tap[-1][-1], np.zeros(par["nt"]))
    assert_array_equal(tap[par["ntap"][0], par["ntap"][1]], np.ones(par["nt"]))
    assert_array_equal(tap[par["nspat"][0] // 2, par["nspat"][1] // 2],
                       np.ones(par["nt"]))
Beispiel #2
0
def Sliding3D(Op, dims, dimsd, nwin, nover, nop,
              tapertype='hanning', design=False, nproc=1):
    """3D Sliding transform operator.

    Apply a transform operator ``Op`` repeatedly to patches of the model
    vector in forward mode and patches of the data vector in adjoint mode.
    More specifically, in forward mode the model vector is divided into patches
    each patch is transformed, and patches are then recombined in a sliding
    window fashion. Both model and data should be 3-dimensional
    arrays in nature as they are internally reshaped and interpreted as
    3-dimensional arrays. Each patch contains in fact a portion of the
    array in the first and second dimensions (and the entire third dimension).

    This operator can be used to perform local, overlapping transforms (e.g.,
    :obj:`pylops.signalprocessing.FFTND`
    or :obj:`pylops.signalprocessing.Radon3D`) of 3-dimensional arrays.

    .. note:: The shape of the model has to be consistent with
       the number of windows for this operator not to return an error. As the
       number of windows depends directly on the choice of ``nwin`` and
       ``nover``, it is recommended to use ``design=True`` if unsure about the
       choice ``dims`` and use the number of windows printed on screen to
       define such input parameter.

    .. warning:: Depending on the choice of `nwin` and `nover` as well as the
       size of the data, sliding windows may not cover the entire first and/or
       second dimensions. The start and end indeces of each window can be
       displayed using ``design=True`` while defining the best sliding window
       approach.

    Parameters
    ----------
    Op : :obj:`pylops.LinearOperator`
        Transform operator
    dims : :obj:`tuple`
        Shape of 3-dimensional model. Note that ``dims[0]`` and ``dims[1]``
        should be multiple of the model sizes of the transform in the
        first and second dimensions
    dimsd : :obj:`tuple`
        Shape of 3-dimensional data
    nwin : :obj:`tuple`
        Number of samples of window
    nover : :obj:`tuple`
        Number of samples of overlapping part of window
    nop : :obj:`tuple`
        Number of samples in axes of transformed domain associated
        to spatial axes in the data
    tapertype : :obj:`str`, optional
        Type of taper (``hanning``, ``cosine``, ``cosinesquare`` or ``None``)
    design : :obj:`bool`, optional
        Print number sliding window (``True``) or not (``False``)

    Returns
    -------
    Sop : :obj:`pylops.LinearOperator`
        Sliding operator

    Raises
    ------
    ValueError
        Identified number of windows is not consistent with provided model
        shape (``dims``).

    """
    # model windows
    mwin0_ins, mwin0_ends = _slidingsteps(dims[0],
                                          Op.shape[1]//(nop[1]*dims[2]), 0)
    mwin1_ins, mwin1_ends = _slidingsteps(dims[1],
                                          Op.shape[1]//(nop[0]*dims[2]), 0)

    # data windows
    dwin0_ins, dwin0_ends = _slidingsteps(dimsd[0], nwin[0], nover[0])
    dwin1_ins, dwin1_ends = _slidingsteps(dimsd[1], nwin[1], nover[1])
    nwins0 = len(dwin0_ins)
    nwins1 = len(dwin1_ins)
    nwins = nwins0*nwins1

    # create tapers
    if tapertype is not None:
        tap = taper3d(dimsd[2], nwin, nover, tapertype=tapertype)

    # check that identified number of windows agrees with mode size
    if design:
        logging.warning('(%d,%d) windows required...', nwins0, nwins1)
        logging.warning('model wins - start0:%s, end0:%s, start1:%s, end1:%s',
                        str(mwin0_ins), str(mwin0_ends),
                        str(mwin1_ins), str(mwin1_ends))
        logging.warning('data wins - start0:%s, end0:%s, start1:%s, end1:%s',
                        str(dwin0_ins), str(dwin0_ends),
                        str(dwin1_ins), str(dwin1_ends))

    if nwins*Op.shape[1]//dims[2] != dims[0]*dims[1]:
        raise ValueError('Model shape (dims=%s) is not consistent with chosen '
                         'number of windows. Choose dims[0]=%d and '
                         'dims[1]=%d for the operator to work with '
                         'estimated number of windows, or create '
                         'the operator with design=True to find out the'
                         'optimal number of windows for the current '
                         'model size...'
                         % (str(dims), nwins0*Op.shape[1]//(nop[1]*dims[2]),
                            nwins1 * Op.shape[1]//(nop[0]*dims[2])))
    # transform to apply
    if tapertype is None:
        OOp = BlockDiag([Op for _ in range(nwins)], nproc=nproc)
    else:
        OOp = BlockDiag([Diagonal(tap.flatten()) * Op
                         for _ in range(nwins)], nproc=nproc)

    hstack = HStack([Restriction(dimsd[1] * dimsd[2] * nwin[0],
                                 range(win_in, win_end),
                                 dims=(nwin[0], dimsd[1], dimsd[2]),
                                 dir=1).H
                     for win_in, win_end in zip(dwin1_ins,
                                                dwin1_ends)])

    combining1 = BlockDiag([hstack]*nwins0)
    combining0 = HStack([Restriction(np.prod(dimsd),
                                     range(win_in, win_end),
                                     dims=dimsd, dir=0).H
                         for win_in, win_end in zip(dwin0_ins, dwin0_ends)])
    Sop = combining0 * combining1 * OOp
    return Sop
Beispiel #3
0
def Deghosting(
    p,
    nt,
    nr,
    dt,
    dr,
    vel,
    zrec,
    pd=None,
    win=None,
    npad=(11, 11),
    ntaper=(11, 11),
    restriction=None,
    sptransf=None,
    solver=lsqr,
    dottest=False,
    dtype="complex128",
    **kwargs_solver
):
    r"""Wavefield deghosting.

    Apply seismic wavefield decomposition from single-component (pressure)
    data. This process is also generally referred to as model-based deghosting.

    Parameters
    ----------
    p : :obj:`np.ndarray`
        Pressure data of of size :math:`\lbrack n_{r_x}\,(\times n_{r_y})
        \times n_t \rbrack` (or :math:`\lbrack n_{r_{x,\text{sub}}}\,
        (\times n_{r_{y,\text{sub}}}) \times n_t \rbrack`
        in case a ``restriction`` operator is provided. Note that
        :math:`n_{r_{x,\text{sub}}}` (and :math:`n_{r_{y,\text{sub}}}`)
        must agree with the size of the output of this operator)
    nt : :obj:`int`
        Number of samples along the time axis
    nr : :obj:`int` or :obj:`tuple`
        Number of samples along the receiver axis (or axes)
    dt : :obj:`float`
        Sampling along the time axis
    dr : :obj:`float` or :obj:`tuple`
        Sampling along the receiver array of the separated
        pressure consituents
    vel : :obj:`float`
        Velocity along the receiver array (must be constant)
    zrec : :obj:`float`
        Depth of receiver array
    pd : :obj:`np.ndarray`, optional
        Direct arrival to be subtracted from ``p``
    win : :obj:`np.ndarray`, optional
        Time window to be applied to ``p`` to remove the direct arrival
        (if ``pd=None``)
    ntaper : :obj:`float` or :obj:`tuple`, optional
        Number of samples of taper applied to propagator to avoid edge
        effects
    npad : :obj:`float` or :obj:`tuple`, optional
        Number of samples of padding applied to propagator to avoid edge
        effects
        angle
    restriction : :obj:`pylops.LinearOperator`, optional
        Restriction operator
    sptransf : :obj:`pylops.LinearOperator`, optional
        Sparsifying operator
    solver : :obj:`float`, optional
        Function handle of solver to be used if ``kind='inverse'``
    dottest : :obj:`bool`, optional
        Apply dot-test
    dtype : :obj:`str`, optional
        Type of elements in input array. If ``None``, directly inferred
        from ``p``
    **kwargs_solver
        Arbitrary keyword arguments for chosen ``solver``

    Returns
    -------
    pup : :obj:`np.ndarray`
        Up-going wavefield
    pdown : :obj:`np.ndarray`
        Down-going wavefield

    Notes
    -----
    Up- and down-going components of seismic data :math:`p^-(x, t)`
    and :math:`p^+(x, t)` can be estimated from single-component data
    :math:`p(x, t)` using a ghost model.

    The basic idea [1]_ is that of using a one-way propagator in the f-k domain
    (also referred to as ghost model) to predict the down-going field
    from the up-going one (excluded the direct arrival and its source
    ghost referred here to as :math:`p_d(x, t)`):

    .. math::
        p^+ - p_d = e^{-j k_z 2 z_\text{rec}} p^-

    where :math:`k_z` is the vertical wavenumber and :math:`z_\text{rec}` is the
    depth of the array of receivers

    In a matrix form we can thus write the total wavefield as:

    .. math::
        \mathbf{p} - \mathbf{p_d} = (\mathbf{I} + \Phi) \mathbf{p}^-

    where :math:`\Phi` is one-way propagator implemented via the
    :class:`pylops.waveeqprocessing.PhaseShift` operator.

    .. [1] Amundsen, L., 1993, Wavenumber-based filtering of marine point-source
       data: GEOPHYSICS, 58, 1335–1348.


    """
    ndims = p.ndim
    if ndims == 2:
        dims = (nt, nr)
        nrs = nr
        nkx = nr + 2 * npad
        kx = np.fft.ifftshift(np.fft.fftfreq(nkx, dr))
        ky = None
    else:
        dims = (nt, nr[0], nr[1])
        nrs = nr[0] * nr[1]
        nkx = nr[0] + 2 * npad[0]
        kx = np.fft.ifftshift(np.fft.fftfreq(nkx, dr[0]))
        nky = nr[1] + 2 * npad[1]
        ky = np.fft.ifftshift(np.fft.fftfreq(nky, dr))
    nf = nt
    freq = np.fft.rfftfreq(nf, dt)

    # Phase shift operator
    zprop = 2 * zrec
    if ndims == 2:
        taper = taper2d(nt, nr, ntaper).T
        Padop = Pad(dims, ((0, 0), (npad, npad)))
    else:
        taper = taper3d(nt, nr, ntaper).transpose(2, 0, 1)
        Padop = Pad(dims, ((0, 0), (npad[0], npad[0]), (npad[1], npad[1])))

    Pop = (
        -Padop.H
        * PhaseShift(vel, zprop, nt, freq, kx, ky)
        * Padop
        * Diagonal(taper.ravel(), dtype=dtype)
    )

    # Decomposition operator
    Dupop = Identity(nt * nrs, dtype=p.dtype) + Pop
    if dottest:
        Dottest(Dupop, nt * nrs, nt * nrs, verb=True)

    # Add restriction
    if restriction is not None:
        Dupop_norestr = Dupop
        Dupop = restriction * Dupop

    # Add sparsify transform
    if sptransf is not None:
        Dupop_norestr = Dupop_norestr * sptransf
        Dupop = Dupop * sptransf

    # Define data
    if pd is not None:
        d = p - pd
    else:
        d = win * p

    # Inversion
    pup = solver(Dupop, d.ravel(), **kwargs_solver)[0]

    # Apply sparse transform
    if sptransf is not None:
        p = Dupop_norestr * pup  # reconstruct p at finely sampled spatial axes
        pup = sptransf * pup
        p = np.real(p).reshape(dims)

    # Finalize estimates
    pup = np.real(pup).reshape(dims)
    pdown = p - pup

    return pup, pdown
Beispiel #4
0
    "dt": 0.004,
    "nt": 300,
    "f0": 20,
    "nfmax": 200,
}

t0_m = [0.2]
vrms_m = [700.0]
amp_m = [1.0]

t0_G = [0.2, 0.5, 0.7]
vrms_G = [800.0, 1200.0, 1500.0]
amp_G = [1.0, 0.6, 0.5]

# Taper
tap = taper3d(par["nt"], [par["ny"], par["nx"]], (5, 5), tapertype="hanning")

# Create axis
t, t2, x, y = makeaxis(par)

# Create wavelet
wav = ricker(t[:41], f0=par["f0"])[0]

# Generate model
m, mwav = hyperbolic2d(x, t, t0_m, vrms_m, amp_m, wav)

# Generate operator
G, Gwav = np.zeros((par["ny"], par["nx"], par["nt"])), np.zeros(
    (par["ny"], par["nx"], par["nt"]))
for iy, y0 in enumerate(y):
    G[iy], Gwav[iy] = hyperbolic2d(x - y0, t, t0_G, vrms_G, amp_G, wav)
Beispiel #5
0
    'dt': 0.004,
    'nt': 300,
    'f0': 20,
    'nfmax': 200
}

t0_m = [0.2]
vrms_m = [700.]
amp_m = [1.]

t0_G = [0.2, 0.5, 0.7]
vrms_G = [800., 1200., 1500.]
amp_G = [1., 0.6, 0.5]

# Taper
tap = taper3d(par['nt'], [par['ny'], par['nx']], (5, 5), tapertype='hanning')

# Create axis
t, t2, x, y = makeaxis(par)

# Create wavelet
wav = ricker(t[:41], f0=par['f0'])[0]

# Generate model
m, mwav = hyperbolic2d(x, t, t0_m, vrms_m, amp_m, wav)

# Generate operator
G, Gwav = np.zeros((par['ny'], par['nx'], par['nt'])), \
          np.zeros((par['ny'], par['nx'], par['nt']))
for iy, y0 in enumerate(y):
    G[iy], Gwav[iy] = hyperbolic2d(x - y0, t, t0_G, vrms_G, amp_G, wav)