Ejemplo n.º 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
Ejemplo n.º 2
0
def _PoststackLinearModelling(wav,
                              nt0,
                              spatdims=None,
                              explicit=False,
                              sparse=False,
                              _MatrixMult=MatrixMult,
                              _Convolve1D=Convolve1D,
                              _FirstDerivative=FirstDerivative,
                              args_MatrixMult={},
                              args_Convolve1D={},
                              args_FirstDerivative={}):
    """Post-stack linearized seismic modelling operator.

    Used to be able to provide operators from different libraries to
    PoststackLinearModelling. It operates in the same way as public method
    (PoststackLinearModelling) but has additional input parameters allowing
    passing a different operator and additional arguments to be passed to such
    operator.

    """
    if len(wav.shape) == 2 and wav.shape[0] != nt0:
        raise ValueError('Provide 1d wavelet or 2d wavelet composed of nt0 '
                         'wavelets')

    # organize dimensions
    if spatdims is None:
        dims = (nt0, )
        spatdims = None
    elif isinstance(spatdims, int):
        dims = (nt0, spatdims)
        spatdims = (spatdims, )
    else:
        dims = (nt0, ) + 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

        # Create wavelet operator
        if len(wav.shape) == 1:
            C = convmtx(wav, nt0)[:, len(wav) // 2:-len(wav) // 2 + 1]
        else:
            C = nonstationary_convmtx(wav,
                                      nt0,
                                      hc=wav.shape[1] // 2,
                                      pad=(nt0, nt0))
        # Combine operators
        M = np.dot(C, D)
        if sparse:
            M = csc_matrix(M)
        Pop = _MatrixMult(M, dims=spatdims, **args_MatrixMult)
    else:
        # Create wavelet operator
        if len(wav.shape) == 1:
            Cop = _Convolve1D(np.prod(np.array(dims)),
                              h=wav,
                              offset=len(wav) // 2,
                              dir=0,
                              dims=dims,
                              **args_Convolve1D)
        else:
            Cop = _MatrixMult(nonstationary_convmtx(wav,
                                                    nt0,
                                                    hc=wav.shape[1] // 2,
                                                    pad=(nt0, nt0)),
                              dims=spatdims,
                              **args_MatrixMult)
        # Create derivative operator
        Dop = _FirstDerivative(np.prod(np.array(dims)),
                               dims=dims,
                               dir=0,
                               sampling=1.,
                               **args_FirstDerivative)
        Pop = Cop * Dop
    return Pop
Ejemplo n.º 3
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
Ejemplo n.º 4
0
def PoststackLinearModelling(wav, nt0, ndims=None, explicit=False):
    r"""Post-stack linearized seismic modelling operator.

    Create operator to be applied to acoustic impedance profile
    for generation of band-limited seismic post-stack data.

    Parameters
    ----------
    wav : :obj:`np.ndarray`
        Wavelet in time domain (must had odd number of elements
        and centered to zero)
    nt0 : :obj:`int`
        Number of samples along time axis
    ndims : :obj:`int` or :obj:`tuple`
        Number of samples along horizontal axws
    explicit : :obj:`bool`, optional
        Create a chained linear operator (``False``, preffered for large data)
        or a ``MatrixMult`` linear operator with dense matrix (``True``,
        preffered for small data)

    Returns
    -------
    Postop : :obj:`LinearOperator`
        post-stack modelling operator.

    Notes
    -----
    Post-stack seismic modelling is the process of constructing
    seismic post-stack data from a profile of acoustic impedance in
    time (or depth) domain. This can be easily achieved using the
    following forward model:

    .. math::
        d(t, \theta=0) = w(t) * \frac{dln(AI(t))}{dt}

    where :math:`AI(t)` is the acoustic impedance profile and
    :math:`w(t)` is the time domain seismic wavelet. In compact form:

    .. math::
        \mathbf{d}= \mathbf{W} \mathbf{AI}

    On the other hand, post-stack inversion aims at recovering
    the impedance profile from the band-limited seismic stack data.

    """
    if ndims is None:
        nspat = (1, )
        ndims = (nt0, )
    elif isinstance(ndims, int):
        nspat = (ndims, )
        ndims = (nt0, ndims)
    else:
        nspat = ndims
        ndims = (nt0, ) + ndims
    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

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

        # Combine operators
        M = np.dot(C, D)
        return MatrixMult(M, dims=nspat)
    else:
        # Create wavelet operator
        Cop = Convolve1D(np.prod(np.array(ndims)),
                         h=wav,
                         offset=len(wav) // 2,
                         dir=0,
                         dims=ndims)

        # Create derivative operator
        Dop = FirstDerivative(np.prod(np.array(ndims)),
                              dims=ndims,
                              dir=0,
                              sampling=1.)

        return Cop * Dop
Ejemplo n.º 5
0
def _PoststackLinearModelling(wav,
                              nt0,
                              spatdims=None,
                              explicit=False,
                              sparse=False,
                              kind='centered',
                              _MatrixMult=MatrixMult,
                              _Convolve1D=Convolve1D,
                              _FirstDerivative=FirstDerivative,
                              args_MatrixMult={},
                              args_Convolve1D={},
                              args_FirstDerivative={}):
    """Post-stack linearized seismic modelling operator.

    Used to be able to provide operators from different libraries to
    PoststackLinearModelling. It operates in the same way as public method
    (PoststackLinearModelling) but has additional input parameters allowing
    passing a different operator and additional arguments to be passed to such
    operator.

    """
    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 = wav.dtype  # ensure wav.dtype rules that of operator

    if len(wav.shape) == 2 and wav.shape[0] != nt0:
        raise ValueError('Provide 1d wavelet or 2d wavelet composed of nt0 '
                         'wavelets')

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

    if explicit:
        # 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), -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

        # Create wavelet operator
        if len(wav.shape) == 1:
            C = convmtx(wav, nt0)[:, len(wav) // 2:-len(wav) // 2 + 1]
        else:
            C = nonstationary_convmtx(wav,
                                      nt0,
                                      hc=wav.shape[1] // 2,
                                      pad=(nt0, nt0))
        # Combine operators
        M = ncp.dot(C, D)
        if sparse:
            M = get_csc_matrix(wav)(M)
        Pop = _MatrixMult(M, dims=spatdims, dtype=dtype, **args_MatrixMult)
    else:
        # Create wavelet operator
        if len(wav.shape) == 1:
            Cop = _Convolve1D(np.prod(np.array(dims)),
                              h=wav,
                              offset=len(wav) // 2,
                              dir=0,
                              dims=dims,
                              dtype=dtype,
                              **args_Convolve1D)
        else:
            Cop = _MatrixMult(nonstationary_convmtx(wav,
                                                    nt0,
                                                    hc=wav.shape[1] // 2,
                                                    pad=(nt0, nt0)),
                              dims=spatdims,
                              dtype=dtype,
                              **args_MatrixMult)
        # Create derivative operator
        Dop = _FirstDerivative(np.prod(np.array(dims)),
                               dims=dims,
                               dir=0,
                               sampling=1.,
                               kind=kind,
                               dtype=dtype,
                               **args_FirstDerivative)
        Pop = Cop * Dop
    return Pop
Ejemplo n.º 6
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
Ejemplo n.º 7
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`
    theta : :obj:`int`
        Incident angles in degrees
    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

    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.

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

    # 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 infinite-reflectivity data
    M = np.dot(G, np.dot(D, m.T.flatten())).reshape(ntheta, nt0)
    Mconv = VStack([
        MatrixMult(convmtx(M[itheta], nwav)[wavc:-nwav + wavc + 1])
        for itheta in range(ntheta)
    ])

    return Mconv
Ejemplo n.º 8
0
def PoststackLinearModelling(wav, nt0, spatdims=None, explicit=False,
                             sparse=False):
    r"""Post-stack linearized seismic modelling operator.

    Create operator to be applied to an acoustic impedance trace (or stack of
    traces) for generation of band-limited seismic post-stack data. The input
    model and data have shape :math:`[n_{t0} (\times n_x \times n_y)]`.

    Parameters
    ----------
    wav : :obj:`np.ndarray`
        Wavelet in time domain (must had odd number of elements
        and centered to zero)
    nt0 : :obj:`int`
        Number of samples along time axis
    spatdims : :obj:`int` or :obj:`tuple`, optional
        Number of samples along spatial axis (or axes)
        (``None`` if only one dimension is available)
    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)
    sparse : :obj:`bool`, optional
        Create a sparse matrix (``True``) or dense  (``False``) when
        ``explicit=True``

    Returns
    -------
    Pop : :obj:`LinearOperator`
        post-stack modelling operator.

    Notes
    -----
    Post-stack seismic modelling is the process of constructing
    seismic post-stack data from a profile of acoustic impedance in
    time (or depth) domain. This can be easily achieved using the
    following forward model:

    .. math::
        d(t, \theta=0) = w(t) * \frac{dln(AI(t))}{dt}

    where :math:`AI(t)` is the acoustic impedance profile and
    :math:`w(t)` is the time domain seismic wavelet. In compact form:

    .. math::
        \mathbf{d}= \mathbf{W} \mathbf{D} \mathbf{ai}

    On the other hand, post-stack inversion aims at recovering
    the impedance profile from the band-limited seismic stack data.

    """
    # organize dimensions
    if spatdims is None:
        dims = (nt0, )
        spatdims = None
    elif isinstance(spatdims, int):
        dims = (nt0, spatdims)
        spatdims = (spatdims,)
    else:
        dims = (nt0, ) + 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

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

        # Combine operators
        M = np.dot(C, D)
        if sparse:
            M = csc_matrix(M)
        Pop = 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 derivative operator
        Dop = FirstDerivative(np.prod(np.array(dims)), dims=dims,
                              dir=0, sampling=1.)
        Pop = Cop*Dop
    return Pop