예제 #1
0
def PrestackLinearModelling(wav,
                            theta,
                            vsvp=0.5,
                            nt0=1,
                            spatdims=None,
                            linearization='akirich',
                            explicit=False,
                            kind='centered'):
    r"""Pre-stack linearized seismic modelling operator.

    Create operator to be applied to elastic property profiles
    for generation of band-limited seismic angle gathers from a
    linearized version of the Zoeppritz equation. The input model must
    be arranged in a vector of size :math:`n_m \times n_{t0} (\times n_x \times n_y)`
    for ``explicit=True`` and :math:`n_{t0} \times n_m  (\times n_x \times n_y)`
    for ``explicit=False``. Similarly the output data is arranged in a
    vector of size :math:`n_{theta} \times n_{t0}  (\times n_x \times n_y)`
    for ``explicit=True`` and :math:`n_{t0} \times n_{theta} (\times n_x \times n_y)`
    for ``explicit=False``.

    Parameters
    ----------
    wav : :obj:`np.ndarray`
        Wavelet in time domain (must had odd number of
        elements and centered to zero). Note that the ``dtype`` of this
        variable will define that of the operator
    theta : :obj:`np.ndarray`
        Incident angles in degrees. Must have same ``dtype`` of ``wav`` (or
        it will be automatically casted to it)
    vsvp : :obj:`float` or :obj:`np.ndarray`
        VS/VP ratio (constant or time/depth variant)
    nt0 : :obj:`int`, optional
        number of samples (if ``vsvp`` is a scalar)
    spatdims : :obj:`int` or :obj:`tuple`, optional
        Number of samples along spatial axis (or axes)
        (``None`` if only one dimension is available)
    linearization : :obj:`str` or :obj:`func`, optional
        choice of linearization, ``akirich``: Aki-Richards, ``fatti``: Fatti,
        ``ps``: PS or any function on the form of
        ``pylops.avo.avo.akirichards``
    explicit : :obj:`bool`, optional
        Create a chained linear operator (``False``, preferred for large data)
        or a ``MatrixMult`` linear operator with dense matrix
        (``True``, preferred for small data)
    kind : :obj:`str`, optional
        Derivative kind (``forward`` or ``centered``).

    Returns
    -------
    Preop : :obj:`LinearOperator`
        pre-stack modelling operator.

    Raises
    ------
    NotImplementedError
        If ``linearization`` is not an implemented linearization
    NotImplementedError
        If ``kind`` is not ``forward`` nor ``centered``

    Notes
    -----
    Pre-stack seismic modelling is the process of constructing seismic
    pre-stack data from three (or two) profiles of elastic parameters in time
    (or depth) domain. This can be easily achieved using the following
    forward model:

    .. math::
        d(t, \theta) = w(t) * \sum_{i=1}^{n_m} G_i(t, \theta) m_i(t)

    where :math:`w(t)` is the time domain seismic wavelet. In compact form:

    .. math::
        \mathbf{d}= \mathbf{G} \mathbf{m}

    On the other hand, pre-stack inversion aims at recovering the different
    profiles of elastic properties from the band-limited seismic
    pre-stack data.

    """
    ncp = get_array_module(wav)

    # check kind is correctly selected
    if kind not in ['forward', 'centered']:
        raise NotImplementedError('%s not an available derivative kind...' %
                                  kind)
    # define dtype to be used
    dtype = theta.dtype  # ensure theta.dtype rules that of operator
    theta = theta.astype(dtype)

    # create vsvp profile
    vsvp = vsvp if isinstance(vsvp, ncp.ndarray) else \
        vsvp * ncp.ones(nt0, dtype=dtype)
    nt0 = len(vsvp)
    ntheta = len(theta)

    # organize dimensions
    if spatdims is None:
        dims = (nt0, ntheta)
        spatdims = None
    elif isinstance(spatdims, int):
        dims = (nt0, ntheta, spatdims)
        spatdims = (spatdims, )
    else:
        dims = (nt0, ntheta) + spatdims

    if explicit:
        # Create AVO operator
        if linearization == 'akirich':
            G = akirichards(theta, vsvp, n=nt0)
        elif linearization == 'fatti':
            G = fatti(theta, vsvp, n=nt0)
        elif linearization == 'ps':
            G = ps(theta, vsvp, n=nt0)
        elif callable(linearization):
            G = linearization(theta, vsvp, n=nt0)
        else:
            logging.error('%s not an available linearization...',
                          linearization)
            raise NotImplementedError('%s not an available linearization...' %
                                      linearization)
        nG = len(G)
        G = [
            ncp.hstack([
                ncp.diag(G_[itheta] * ncp.ones(nt0, dtype=dtype)) for G_ in G
            ]) for itheta in range(ntheta)
        ]
        G = ncp.vstack(G).reshape(ntheta * nt0, nG * nt0)

        # Create derivative operator
        if kind == 'centered':
            D = ncp.diag(0.5 * ncp.ones(nt0 - 1, dtype=dtype), k=1) - \
                ncp.diag(0.5 * ncp.ones(nt0 - 1, dtype=dtype), k=-1)
            D[0] = D[-1] = 0
        else:
            D = ncp.diag(ncp.ones(nt0 - 1, dtype=dtype), k=1) - \
                ncp.diag(ncp.ones(nt0, dtype=dtype), k=0)
            D[-1] = 0
        D = get_block_diag(theta)(*([D] * nG))

        # Create wavelet operator
        C = convmtx(wav, nt0)[:, len(wav) // 2:-len(wav) // 2 + 1]
        C = [C] * ntheta
        C = get_block_diag(theta)(*C)

        # Combine operators
        M = ncp.dot(C, ncp.dot(G, D))
        Preop = MatrixMult(M, dims=spatdims, dtype=dtype)

    else:
        # Create wavelet operator
        Cop = Convolve1D(np.prod(np.array(dims)),
                         h=wav,
                         offset=len(wav) // 2,
                         dir=0,
                         dims=dims,
                         dtype=dtype)

        # create AVO operator
        AVOop = AVOLinearModelling(theta,
                                   vsvp,
                                   spatdims=spatdims,
                                   linearization=linearization,
                                   dtype=dtype)

        # Create derivative operator
        dimsm = list(dims)
        dimsm[1] = AVOop.npars
        Dop = FirstDerivative(np.prod(np.array(dimsm)),
                              dims=dimsm,
                              dir=0,
                              sampling=1.,
                              kind=kind,
                              dtype=dtype)
        Preop = Cop * AVOop * Dop
    return Preop
예제 #2
0
def PrestackLinearModelling(wav,
                            theta,
                            vsvp=0.5,
                            nt0=1,
                            spatdims=None,
                            linearization='akirich',
                            explicit=False):
    r"""Pre-stack linearized seismic modelling operator.

    Create operator to be applied to elastic property profiles
    for generation of band-limited seismic angle gathers from a
    linearized version of the Zoeppritz equation.

    Parameters
    ----------
    wav : :obj:`np.ndarray`
        Wavelet in time domain (must had odd number of
        elements and centered to zero)
    theta : :obj:`np.ndarray`
        Incident angles in degrees
    vsvp : :obj:`float` or :obj:`np.ndarray`
        VS/VP ratio (constant or time/depth variant)
    nt0 : :obj:`int`, optional
        number of samples (if ``vsvp`` is a scalar)
    spatdims : :obj:`int` or :obj:`tuple`, optional
        Number of samples along spatial axis (or axes)
        (``None`` if only one dimension is available)
    linearization : :obj:`str`, optional
        choice of linearization, ``akirich``: Aki-Richards, ``fatti``: Fatti
    explicit : :obj:`bool`, optional
        Create a chained linear operator (``False``, preferred for large data)
        or a ``MatrixMult`` linear operator with dense matrix
        (``True``, preferred for small data)

    Returns
    -------
    Preop : :obj:`LinearOperator`
        pre-stack modelling operator.

    Raises
    ------
    NotImplementedError
        If ``linearization`` is not an implemented linearization

    Notes
    -----
    Pre-stack seismic modelling is the process of constructing seismic
    pre-stack data from three (or two) profiles of elastic parameters in time
    (or depth) domain arranged in an input vector :math:`\mathbf{m}` of size
    :math:`nt0 \times N`. This can be easily achieved using the following
    forward model:

    .. math::
        d(t, \theta) = w(t) * \sum_{i=1}^N G_i(t, \theta) m_i(t)

    where :math:`w(t)` is the time domain seismic wavelet. In compact form:

    .. math::
        \mathbf{d}= \mathbf{G} \mathbf{m}

    On the other hand, pre-stack inversion aims at recovering the different
    profiles of elastic properties from the band-limited seismic
    pre-stack data.

    """
    # create vsvp profile
    vsvp = vsvp if isinstance(vsvp, np.ndarray) else vsvp * np.ones(nt0)
    nt0 = len(vsvp)
    ntheta = len(theta)

    # organize dimensions
    if spatdims is None:
        dims = (nt0, ntheta)
        spatdims = None
    elif isinstance(spatdims, int):
        dims = (nt0, ntheta, spatdims)
        spatdims = (spatdims, )
    else:
        dims = (nt0, ntheta) + spatdims

    if explicit:
        # Create derivative operator
        D = np.diag(0.5 * np.ones(nt0 - 1), k=1) - \
            np.diag(0.5 * np.ones(nt0 - 1), -1)
        D[0] = D[-1] = 0
        D = block_diag(*([D] * 3))

        # Create AVO operator
        if linearization == 'akirich':
            G1, G2, G3 = akirichards(theta, vsvp, n=nt0)
        elif linearization == 'fatti':
            G1, G2, G3 = fatti(theta, vsvp, n=nt0)
        else:
            logging.error('%s not an available linearization...',
                          linearization)
            raise NotImplementedError('%s not an available linearization...' %
                                      linearization)

        G = [
            np.hstack((np.diag(G1[itheta] * np.ones(nt0)),
                       np.diag(G2[itheta] * np.ones(nt0)),
                       np.diag(G3[itheta] * np.ones(nt0))))
            for itheta in range(ntheta)
        ]
        G = np.vstack(G).reshape(ntheta * nt0, 3 * nt0)

        # Create wavelet operator
        C = convmtx(wav, nt0)[:, len(wav) // 2:-len(wav) // 2 + 1]
        C = [C] * ntheta
        C = block_diag(*C)

        # Combine operators
        M = np.dot(C, np.dot(G, D))
        return MatrixMult(M, dims=spatdims)

    else:
        # Create wavelet operator
        Cop = Convolve1D(np.prod(np.array(dims)),
                         h=wav,
                         offset=len(wav) // 2,
                         dir=0,
                         dims=dims)

        # create AVO operator
        AVOop = AVOLinearModelling(theta,
                                   vsvp,
                                   spatdims=spatdims,
                                   linearization=linearization)

        # Create derivative operator
        dimsm = list(dims)
        dimsm[1] = AVOop.npars
        Dop = FirstDerivative(np.prod(np.array(dimsm)),
                              dims=dimsm,
                              dir=0,
                              sampling=1.)
        return Cop * AVOop * Dop