예제 #1
0
def test_Convolve1D(par):
    """Dot-test and inversion for Convolve1D operator
    """
    np.random.seed(10)
    #1D
    if par['dir'] == 0:
        Cop = Convolve1D(par['nx'], h=h1, offset=par['offset'], dtype='float32')
        assert dottest(Cop, par['nx'], par['nx'])

        x = np.zeros((par['nx']))
        x[par['nx']//2] = 1.
        xlsqr = lsqr(Cop, Cop * x, damp=1e-20, iter_lim=200, show=0)[0]
        assert_array_almost_equal(x, xlsqr, decimal=1)

    # 1D on 2D
    Cop = Convolve1D(par['ny'] * par['nx'], h=h1, offset=par['offset'],
                     dims=(par['ny'], par['nx']), dir=par['dir'], dtype='float32')
    assert dottest(Cop, par['ny'] * par['nx'], par['ny'] * par['nx'])

    x = np.zeros((par['ny'], par['nx']))
    x[int(par['ny']/2-3):int(par['ny']/2+3),
      int(par['nx']/2-3):int(par['nx']/2+3)] = 1.
    x = x.flatten()
    xlsqr = lsqr(Cop, Cop * x, damp=1e-20, iter_lim=200, show=0)[0]
    assert_array_almost_equal(x, xlsqr, decimal=1)
예제 #2
0
def test_Convolve1D(par):
    """Dot-test and inversion for Convolve1D operator"""
    np.random.seed(10)
    # 1D
    if par["dir"] == 0:
        Cop = Convolve1D(par["nx"],
                         h=h1,
                         offset=par["offset"],
                         dtype="float64")
        assert dottest(Cop, par["nx"], par["nx"])

        x = np.zeros((par["nx"]))
        x[par["nx"] // 2] = 1.0
        xlsqr = lsqr(Cop, Cop * x, damp=1e-20, iter_lim=200, show=0)[0]
        assert_array_almost_equal(x, xlsqr, decimal=1)

    # 1D on 2D
    if par["dir"] < 2:
        Cop = Convolve1D(
            par["ny"] * par["nx"],
            h=h1,
            offset=par["offset"],
            dims=(par["ny"], par["nx"]),
            dir=par["dir"],
            dtype="float64",
        )
        assert dottest(Cop, par["ny"] * par["nx"], par["ny"] * par["nx"])

        x = np.zeros((par["ny"], par["nx"]))
        x[int(par["ny"] / 2 - 3):int(par["ny"] / 2 + 3),
          int(par["nx"] / 2 - 3):int(par["nx"] / 2 + 3), ] = 1.0
        x = x.ravel()
        xlsqr = lsqr(Cop, Cop * x, damp=1e-20, iter_lim=200, show=0)[0]
        assert_array_almost_equal(x, xlsqr, decimal=1)

    # 1D on 3D
    Cop = Convolve1D(
        par["nz"] * par["ny"] * par["nx"],
        h=h1,
        offset=par["offset"],
        dims=(par["nz"], par["ny"], par["nx"]),
        dir=par["dir"],
        dtype="float64",
    )
    assert dottest(Cop, par["nz"] * par["ny"] * par["nx"],
                   par["nz"] * par["ny"] * par["nx"])

    x = np.zeros((par["nz"], par["ny"], par["nx"]))
    x[int(par["nz"] / 2 - 3):int(par["nz"] / 2 + 3),
      int(par["ny"] / 2 - 3):int(par["ny"] / 2 + 3),
      int(par["nx"] / 2 - 3):int(par["nx"] / 2 + 3), ] = 1.0
    x = x.ravel()
    xlsqr = lsqr(Cop, Cop * x, damp=1e-20, iter_lim=200, show=0)[0]
    assert_array_almost_equal(x, xlsqr, decimal=1)
예제 #3
0
def Smoothing1D(nsmooth, dims, dir=0, dtype='float32'):
    r"""1D Smoothing.

    Apply smoothing to model (and data) along a specific direction of a multi-dimensional array
    depending on the choice of ``dir``.

    Parameters
    ----------
    nsmooth : :obj:`int`
        Lenght of smoothing operator (must be odd)
    dims : :obj:`tuple` or :obj:`int`
        Number of samples for each dimension
    dir : :obj:`int`, optional
        Direction along which smoothing is applied
    dtype : :obj:`str`, optional
        Type of elements in input array.

    Attributes
    ----------
    shape : :obj:`tuple`
        Operator shape
    explicit : :obj:`bool`
        Operator contains a matrix that can be solved explicitly (``True``) or not (``False``)

    Notes
    -----
    The Smoothing1D operator is a special type of convolutional operator that convolves
    the input model (or data) with a constant filter of size :math:`n_{smooth}`:

    .. math::
        \mathbf{f} = [ 1/n_{smooth}, 1/n_{smooth}, ..., 1/n_{smooth} ]

    When applied to the first direction:

    .. math::
        y[i,j,k] = 1/n_{smooth} \sum_{l=-(n_{smooth}-1)/2}^{(n_{smooth}-1)/2} x[l,j,k]

    Similarly when applied to the second direction:

    .. math::
        y[i,j,k] = 1/n_{smooth} \sum_{l=-(n_{smooth}-1)/2}^{(n_{smooth}-1)/2} x[i,l,k]

    and the third direction:

    .. math::
        y[i,j,k] = 1/n_{smooth} \sum_{l=-(n_{smooth}-1)/2}^{(n_{smooth}-1)/2} x[i,j,l]

    Note that since the filter is symmetrical, the *Smoothing1D* operator is self-adjoint.

    """
    if isinstance(dims, int): dims = (dims, )
    nsmooth = nsmooth + 1 if nsmooth % 2 == 0 else nsmooth

    return Convolve1D(np.prod(np.array(dims)),
                      dims=dims,
                      dir=dir,
                      h=np.ones(nsmooth) / float(nsmooth),
                      offset=(nsmooth - 1) / 2,
                      dtype=dtype)
예제 #4
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
def test_Convolve1D(par):
    """Dot-test and comparison with Pylops for Convolve1D operator
    """
    np.random.seed(10)
    # 1D
    if par['dir'] == 0:
        Cop = Convolve1D(par['nx'], h=h1, offset=par['offset'],
                         dtype='float32')
        dCop = dConvolve1D(par['nx'], h=h1, offset=par['offset'],
                           compute=(True, True), dtype='float32')
        assert dottest(dCop, par['nx'], par['nx'],
                       chunks=(par['nx']//2 + 1, par['nx']//2 + 1))

        x = np.random.normal(0., 1., par['nx'])
        x = da.from_array(x, chunks=par['nx']//2 + 1)
        dy = dCop * x
        y = Cop * x.compute()
        assert_array_almost_equal(y, dy, decimal=1)

    # 1D on 2D
    if par['dir'] < 2:
        Cop = Convolve1D(par['ny'] * par['nx'], h=h1, offset=par['offset'],
                         dims=(par['ny'], par['nx']), dir=par['dir'],
                         dtype='float32')
        dCop = dConvolve1D(par['ny'] * par['nx'], h=h1, offset=par['offset'],
                           dims=(par['ny'], par['nx']), dir=par['dir'],
                           compute=(True, True),
                           chunks=((par['ny'] // 2 + 1, par['nx'] // 2 + 1),
                                   (par['ny'] // 2 + 1, par['nx'] // 2 + 1)),
                           dtype='float32')
        assert dottest(dCop, par['ny'] * par['nx'], par['ny'] * par['nx'],
                       chunks=(par['ny'] * par['nx'], par['ny'] * par['nx']))

        x = np.random.normal(0., 1., (par['ny'], par['nx']))
        x = da.from_array(x, chunks=(par['ny'] // 2 + 1, par['nx'] // 2 + 1)).flatten()
        dy = dCop * x
        y = Cop * x.compute()
        assert_array_almost_equal(y, dy, decimal=1)

    # 1D on 3D
    Cop = Convolve1D(par['nz'] * par['ny'] * par['nx'], h=h1,
                     offset=par['offset'],
                     dims=(par['nz'], par['ny'], par['nx']), dir=par['dir'],
                     dtype='float32')
    dCop = dConvolve1D(par['nz'] * par['ny'] * par['nx'], h=h1,
                       offset=par['offset'],
                       dims=(par['nz'], par['ny'], par['nx']), dir=par['dir'],
                       compute=(True, True),
                       chunks=((par['nz'] // 2 + 1,
                               par['ny'] // 2 + 1,
                               par['nx'] // 2 + 1),
                               (par['nz'] // 2 + 1,
                                par['ny'] // 2 + 1,
                                par['nx'] // 2 + 1)), dtype='float32')
    assert dottest(dCop, par['nz'] * par['ny'] * par['nx'],
                   par['nz'] * par['ny'] * par['nx'],
                   chunks=(par['nz'] * par['ny'] * par['nx'],
                           par['nz'] * par['ny'] * par['nx']))

    x = np.random.normal(0., 1., (par['nz'], par['ny'], par['nx']))
    x = da.from_array(x, chunks=(par['nz'] // 2 + 1,
                                 par['ny'] // 2 + 1,
                                 par['nx'] // 2 + 1))
    dy = dCop * x.flatten()
    y = Cop * x.compute().flatten()
    assert_array_almost_equal(y, dy, decimal=1)
예제 #6
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
예제 #7
0
def Demigration(z, x, t, srcs, recs, vel, wav, wavcenter,
                y=None, trav=None, mode='eikonal'):
    r"""Kirchoff Demigration operator.

    Traveltime based seismic demigration/migration operator.

    Parameters
    ----------
    z : :obj:`numpy.ndarray`
        Depth axis
    x : :obj:`numpy.ndarray`
        Spatial axis
    t : :obj:`numpy.ndarray`
        Time axis for data
    srcs : :obj:`numpy.ndarray`
        Sources in array of size :math:`\lbrack 2/3 \times n_s \rbrack`
        The first axis should be ordered as (``y``,) ``x``, ``z``.
    recs : :obj:`numpy.ndarray`
        Receivers in array of size :math:`\lbrack 2/3 \times n_r \rbrack`
        The first axis should be ordered as (``y``,) ``x``, ``z``.
    vel : :obj:`numpy.ndarray` or :obj:`float`
        Velocity model of size :math:`\lbrack (n_y \times) n_x
        \times n_z \rbrack` (or constant)
    wav : :obj:`numpy.ndarray`
        Wavelet
    wavcenter : :obj:`int`
        Index of wavelet center
    y : :obj:`numpy.ndarray`
        Additional spatial axis (for 3-dimensional problems)
    mode : :obj:`str`, optional
        Computation mode (``analytic``, ``eikonal`` or ``byot``, see Notes for
        more details)
    trav : :obj:`numpy.ndarray`, optional
        Traveltime table of size
        :math:`\lbrack (n_y*) n_x*n_z \times n_r \rbrack` (to be provided if
        ``mode='byot'``)

    Returns
    -------
    demop : :obj:`pylops.LinearOperator`
        Demigration/Migration operator

    Raises
    ------
    NotImplementedError
        If ``mode`` is neither ``analytic``, ``eikonal``, or ``byot``

    Notes
    -----
    The demigration operator synthetizes seismic data given from a propagation
    velocity model :math:`v` and a reflectivity model :math:`m`. In forward
    mode:

    .. math::
        d(\mathbf{x_r}, \mathbf{x_s}, t) =
        w(t) * \int_V G(\mathbf{x}, \mathbf{x_s}, t)
        G(\mathbf{x_r}, \mathbf{x}, t) m(\mathbf{x}) d\mathbf{x}

    where :math:`m(\mathbf{x})` is the model and it represents the reflectivity
    at every location in the subsurface, :math:`G(\mathbf{x}, \mathbf{x_s}, t)`
    and :math:`G(\mathbf{x_r}, \mathbf{x}, t)` are the Green's functions
    from source-to-subsurface-to-receiver and finally  :math:`w(t)` is the
    wavelet. Depending on the choice of ``mode`` the Green's function will be
    computed and applied differently:

    * ``mode=analytic`` or ``mode=eikonal``: traveltime curves between
      source to receiver pairs are computed for every subsurface point and
      Green's functions are implemented from traveltime look-up tables, placing
      the reflectivity values at corresponding source-to-receiver time in the
      data.
    * ``byot``: bring your own table. Traveltime table provided
      directly by user using ``trav`` input parameter. Green's functions are
      then implemented in the same way as previous options.

    The adjoint of the demigration operator is a *migration* operator which
    projects data in the model domain creating an image of the subsurface
    reflectivity.

    """
    ndim, _, dims, ny, nx, nz, ns, nr, _, _, _, _, _ = \
        _identify_geometry(z, x, srcs, recs, y=y)
    dt = t[1] - t[0]
    nt = len(t)
    if mode in ['analytic', 'eikonal', 'byot']:
        if mode in ['analytic', 'eikonal']:
            # compute traveltime table
            trav = _traveltime_table(z, x, srcs, recs, vel, y=y, mode=mode)[0]

        itrav = (trav / dt).astype('int32')
        travd = (trav / dt - itrav)
        if ndim == 2:
            itrav = itrav.reshape(nx, nz, ns * nr)
            travd = travd.reshape(nx, nz, ns * nr)
            dims = tuple(dims)
        else:
            itrav = itrav.reshape(ny*nx, nz, ns * nr)
            travd = travd.reshape(ny*nx, nz, ns * nr)
            dims = (dims[0]*dims[1], dims[2])

        # create operator
        sop = Spread(dims=dims, dimsd=(ns * nr, nt),
                     table=itrav, dtable=travd, engine='numba')

        cop = Convolve1D(ns * nr * nt, h=wav, offset=wavcenter,
                         dims=(ns * nr, nt),
                         dir=1)
        demop = cop * sop
    else:
        raise NotImplementedError('method must be analytic or eikonal')
    return demop
예제 #8
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
예제 #9
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
예제 #10
0
itrav_tmp = (trav_tmp / dt).astype('int32')
travd_tmp = (trav_tmp / dt - itrav_tmp)
if ndim == 2:
    itrav_tmp = itrav_tmp.reshape(nx, nz, ns * nr)
    travd_tmp = travd_tmp.reshape(nx, nz, ns * nr)
    dims = tuple(dims)
else:
    itrav_tmp = itrav_tmp.reshape(ny*nx, nz, ns * nr)
    travd_tmp = travd_tmp.reshape(ny*nx, nz, ns * nr)
    dims = (dims[0]*dims[1], dims[2])

# create operator
sop = Spread(dims=dims, dimsd=(ns * nr, nt), table=itrav_tmp, dtable=travd_tmp, 
             engine='numba')

cop = Convolve1D(ns * nr * nt, h=wav, offset=wavcenter, dims=(ns * nr, nt),
                 dir=1)
demop = cop * sop


#%%
madj = lsm.Demop.H * d.ravel()
madj = madj.reshape(nx, nz)

plt.figure(figsize=(10,5))
im = plt.imshow(madj.T, cmap='gray')
plt.colorbar(im)
plt.axis('tight')
plt.xlabel('x [m]'),plt.ylabel('y [m]')
plt.title('madj')

# demigration
예제 #11
0
def test_Convolve1D(par):
    """Dot-test, comparison with pylops and inversion for Convolve1D
    operator
    """
    np.random.seed(10)

    #1D
    if par['dir'] == 0:
        gCop = gConvolve1D(par['nx'],
                           h=h1,
                           offset=par['offset'],
                           dtype=torch.float32)
        assert dottest(gCop, par['nx'], par['nx'], tol=1e-3)

        x = torch.zeros((par['nx']), dtype=torch.float32)
        x[par['nx'] // 2] = 1.

        # comparison with pylops
        Cop = Convolve1D(par['nx'],
                         h=h1.cpu().numpy(),
                         offset=par['offset'],
                         dtype='float32')
        assert_array_almost_equal(gCop * x, Cop * x.cpu().numpy(), decimal=3)
        #assert_array_equal(gCop * x, Cop * x.cpu().numpy())

        # inversion
        if par['offset'] == nfilt[0] // 2:
            # zero phase
            xcg = cg(gCop, gCop * x, niter=100)[0]
        else:
            # non-zero phase
            xcg = cg(gCop.H * gCop, gCop.H * (gCop * x), niter=100)[0]
        assert_array_almost_equal(x, xcg, decimal=1)

    # 1D on 2D
    gCop = gConvolve1D(par['ny'] * par['nx'],
                       h=h1,
                       offset=par['offset'],
                       dims=(par['ny'], par['nx']),
                       dir=par['dir'],
                       dtype=torch.float32)
    assert dottest(gCop,
                   par['ny'] * par['nx'],
                   par['ny'] * par['nx'],
                   tol=1e-3)

    x = torch.zeros((par['ny'], par['nx']), dtype=torch.float32)
    x[int(par['ny'] / 2 - 3):int(par['ny'] / 2 + 3),
      int(par['nx'] / 2 - 3):int(par['nx'] / 2 + 3)] = 1.
    x = x.flatten()

    # comparison with pylops
    Cop = Convolve1D(par['ny'] * par['nx'],
                     h=h1.cpu().numpy(),
                     offset=par['offset'],
                     dims=(par['ny'], par['nx']),
                     dir=par['dir'],
                     dtype='float32')
    assert_array_almost_equal(gCop * x, Cop * x.cpu().numpy(), decimal=3)
    # assert_array_equal(gCop * x, Cop * x.cpu().numpy())

    # inversion
    if par['offset'] == nfilt[0] // 2:
        # zero phase
        xcg = cg(gCop, gCop * x, niter=100)[0]
    else:
        # non-zero phase
        xcg = cg(gCop.H * gCop, gCop.H * (gCop * x), niter=100)[0]
    assert_array_almost_equal(x, xcg, decimal=1)

    # 1D on 3D
    gCop = gConvolve1D(par['nz'] * par['ny'] * par['nx'],
                       h=h1,
                       offset=par['offset'],
                       dims=(par['nz'], par['ny'], par['nx']),
                       dir=par['dir'],
                       dtype=torch.float32)
    assert dottest(gCop,
                   par['nz'] * par['ny'] * par['nx'],
                   par['nz'] * par['ny'] * par['nx'],
                   tol=1e-3)

    x = torch.zeros((par['nz'], par['ny'], par['nx']), dtype=torch.float32)
    x[int(par['nz'] / 2 - 3):int(par['nz'] / 2 + 3),
      int(par['ny'] / 2 - 3):int(par['ny'] / 2 + 3),
      int(par['nx'] / 2 - 3):int(par['nx'] / 2 + 3)] = 1.
    x = x.flatten()

    # comparison with pylops
    Cop = Convolve1D(par['nz'] * par['ny'] * par['nx'],
                     h=h1.cpu().numpy(),
                     offset=par['offset'],
                     dims=(par['nz'], par['ny'], par['nx']),
                     dir=par['dir'],
                     dtype='float32')
    assert_array_almost_equal(gCop * x, Cop * x.cpu().numpy(), decimal=3)

    # inversion
    if par['offset'] == nfilt[0] // 2:
        # zero phase
        xcg = cg(gCop, gCop * x, niter=100)[0]
    else:
        # non-zero phase
        xcg = cg(gCop.H * gCop, gCop.H * (gCop * x), niter=100)[0]
    assert_array_almost_equal(x, xcg, decimal=1)