示例#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 PrestackWaveletModelling(m,
                             theta,
                             nwav,
                             wavc=None,
                             vsvp=0.5,
                             linearization='akirich'):
    r"""Pre-stack linearized seismic modelling operator for wavelet.

    Create operator to be applied to a wavelet for generation of
    band-limited seismic angle gathers using a linearized version
    of the Zoeppritz equation.

    Parameters
    ----------
    m : :obj:`np.ndarray`
        elastic parameter profles of size :math:`[n_{t0} \times N]`
        where :math:`N=3/2`. Note that the ``dtype`` of this
        variable will define that of the operator
    theta : :obj:`int`
        Incident angles in degrees. Must have same ``dtype`` of ``m`` (or
        it will be automatically casted to it)
    nwav : :obj:`np.ndarray`
        Number of samples of wavelet to be applied/estimated
    wavc : :obj:`int`, optional
        Index of the center of the wavelet
    vsvp : :obj:`np.ndarray` or :obj:`float`, optional
        VS/VP ratio
    linearization : :obj:`str`, optional
        choice of linearization, ``akirich``: Aki-Richards,
        ``fatti``: Fatti, ``ps``: PS, or any function on the form of
        ``pylops.avo.avo.akirichards``

    Returns
    -------
    Mconv : :obj:`LinearOperator`
        pre-stack modelling operator for wavelet estimation.

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

    Notes
    -----
    Pre-stack seismic modelling for wavelet estimate is the process
    of constructing seismic reflectivities using 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`:

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

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

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

    On the other hand, pre-stack wavelet estimation aims at
    recovering the wavelet given knowledge of the band-limited
    seismic pre-stack data and the elastic parameter profiles.

    """
    ncp = get_array_module(theta)

    # define dtype to be used
    dtype = m.dtype  # ensure m.dtype rules that of operator
    theta = theta.astype(dtype)

    # Create vsvp profile
    vsvp = vsvp if isinstance(vsvp, ncp.ndarray) else \
        vsvp * ncp.ones(m.shape[0], dtype=dtype)
    wavc = nwav // 2 if wavc is None else wavc
    nt0 = len(vsvp)
    ntheta = len(theta)

    # 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
    D = ncp.diag(0.5 * np.ones(nt0 - 1, dtype=dtype), k=1) - \
        ncp.diag(0.5 * np.ones(nt0 - 1, dtype=dtype), k=-1)
    D[0] = D[-1] = 0
    D = get_block_diag(theta)(*([D] * nG))

    # Create infinite-reflectivity data
    M = ncp.dot(G, ncp.dot(D, m.T.flatten())).reshape(ntheta, nt0)
    Mconv = VStack([
        MatrixMult(convmtx(M[itheta], nwav)[wavc:-nwav + wavc + 1],
                   dtype=dtype) for itheta in range(ntheta)
    ])
    return Mconv