def test_MDC_Nvirtualsources(par):
    """Dot-test and comparison with pylops for MDC operator of N virtual source
    """
    if par['twosided']:
        par['nt2'] = 2*par['nt'] - 1
    else:
        par['nt2'] = par['nt']
    v = 1500
    it0_m = 25
    t0_m = it0_m * par['dt']
    theta_m = 0
    phi_m = 0
    amp_m = 1.

    it0_G = np.array([25, 50, 75])
    t0_G = it0_G * par['dt']
    theta_G = (0, 0, 0)
    phi_G = (0, 0, 0)
    amp_G = (1., 0.6, 2.)

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

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

    # Generate model
    _, mwav = linear3d(x, x, t, v, t0_m, theta_m, phi_m, amp_m, wav)

    # Generate operator
    _, Gwav = linear3d(x, y, t, v, t0_G, theta_G, phi_G, amp_G, wav)

    # Add negative part to data and model
    if par['twosided']:
        mwav = np.concatenate((np.zeros((par['nx'], par['nx'], par['nt'] - 1)),
                               mwav), axis=-1)
        Gwav = np.concatenate((np.zeros((par['ny'], par['nx'], par['nt'] - 1)),
                               Gwav), axis=-1)

    # Define MDC linear operator
    Gwav_fft = np.fft.fft(Gwav, par['nt2'], axis=-1)
    Gwav_fft = Gwav_fft[..., :par['nfmax']]

    dMDCop = dMDC(da.from_array(Gwav_fft.transpose(2, 0, 1)), nt=par['nt2'],
                  nv=par['nx'], dt=par['dt'], dr=par['dx'],
                  twosided=par['twosided'])
    MDCop = MDC(Gwav_fft.transpose(2, 0, 1), nt=par['nt2'], nv=par['nx'],
                dt=par['dt'], dr=par['dx'], twosided=par['twosided'],
                transpose=False, dtype='float32')

    dottest(dMDCop, par['nt2'] * par['ny'] * par['nx'],
            par['nt2'] * par['nx'] * par['nx'],
            chunks=((par['nt2'] * par['ny'] * par['nx'],
                     par['nt2'] * par['nx'] * par['nx'])))

    mwav = mwav.T
    dy = (dMDCop * da.from_array(mwav.flatten())).compute()
    y = MDCop * mwav.flatten()
    assert_array_almost_equal(dy, y, decimal=5)
Exemple #2
0
def test_MDC_1virtualsource(par):
    """Dot-test and inversion for MDC operator of 1 virtual source
    """
    if par['twosided']:
        par['nt2'] = 2*par['nt'] - 1
    else:
        par['nt2'] = par['nt']
    v = 1500
    t0_m = 0.2
    theta_m = 0
    amp_m = 1.

    t0_G = (0.1, 0.2, 0.3)
    theta_G = (0, 0, 0)
    phi_G = (0, 0, 0)
    amp_G = (1., 0.6, 2.)

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

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

    # Generate model
    _, mwav = linear2d(x, t, v, t0_m, theta_m, amp_m, wav)
    # Generate operator
    _, Gwav = linear3d(x, y, t, v, t0_G, theta_G, phi_G, amp_G, wav)

    # Add negative part to data and model
    if par['twosided']:
        mwav = np.concatenate((np.zeros((par['nx'], par['nt'] - 1)), mwav), axis=-1)
        Gwav = np.concatenate((np.zeros((par['ny'], par['nx'], par['nt'] - 1)), Gwav), axis=-1)

    # Define MDC linear operator
    Gwav_fft = np.fft.fft(Gwav, par['nt2'], axis=-1)
    Gwav_fft = Gwav_fft[..., :par['nfmax']]

    MDCop = MDC(Gwav_fft, nt=par['nt2'], nv=1,
                dt=par['dt'], dr=par['dx'],
                twosided=par['twosided'], dtype='float32')
    dottest(MDCop, par['nt2']*par['ny'], par['nt2']*par['nx'])

    # Create data
    d = MDCop * mwav.flatten()
    d = d.reshape(par['ny'], par['nt2'])

    # Apply mdd function
    minv = MDD(Gwav[:, :, par['nt']-1:] if par['twosided'] else Gwav,
               d[:, par['nt']-1:] if par['twosided'] else d,
               dt=par['dt'], dr=par['dx'], nfmax=par['nfmax'],
               twosided=par['twosided'], adjoint=False, psf=False, dtype='complex64',
               dottest=False,
               **dict(damp=1e-10, iter_lim=50, show=1))
    assert_array_almost_equal(mwav, minv, decimal=2)
def test_MDC_1virtualsource(par):
    """Dot-test and inversion for MDC operator of 1 virtual source"""
    if par["twosided"]:
        par["nt2"] = 2 * par["nt"] - 1
    else:
        par["nt2"] = par["nt"]
    v = 1500
    it0_m = 25
    t0_m = it0_m * par["dt"]
    theta_m = 0
    amp_m = 1.0

    it0_G = np.array([25, 50, 75])
    t0_G = it0_G * par["dt"]
    theta_G = (0, 0, 0)
    phi_G = (0, 0, 0)
    amp_G = (1.0, 0.6, 2.0)

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

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

    # Generate model
    _, mwav = linear2d(x, t, v, t0_m, theta_m, amp_m, wav)
    # Generate operator
    _, Gwav = linear3d(x, y, t, v, t0_G, theta_G, phi_G, amp_G, wav)

    # Add negative part to data and model
    if par["twosided"]:
        mwav = np.concatenate((np.zeros((par["nx"], par["nt"] - 1)), mwav),
                              axis=-1)
        Gwav = np.concatenate((np.zeros(
            (par["ny"], par["nx"], par["nt"] - 1)), Gwav),
                              axis=-1)

    # Define MDC linear operator
    Gwav_fft = np.fft.fft(Gwav, par["nt2"], axis=-1)
    Gwav_fft = Gwav_fft[..., :par["nfmax"]]
    MDCop = MDC(
        Gwav_fft,
        nt=par["nt2"],
        nv=1,
        dt=par["dt"],
        dr=par["dx"],
        fftengine="fftw",
        twosided=par["twosided"],
        dtype="float32",
    )
    dottest(MDCop, par["nt2"] * par["ny"], par["nt2"] * par["nx"])
    # Create data
    d = MDCop * mwav.ravel()
    d = d.reshape(par["ny"], par["nt2"])

    # Check that events are at correct time and correct amplitude
    for it, amp in zip(it0_G, amp_G):
        ittot = it0_m + it
        if par["twosided"]:
            ittot += par["nt"] - 1
        assert (np.abs(d[par["ny"] // 2, ittot] -
                       np.abs(wav**2).sum() * amp_m * amp * par["nx"] *
                       par["dx"] * par["dt"] * np.sqrt(par["nt2"])) < 1e-2)

    # Check that MDC with prescaled=True gives same result
    MDCpreop = MDC(
        np.sqrt(par["nt2"]) * par["dt"] * par["dx"] * Gwav_fft,
        nt=par["nt2"],
        nv=1,
        dt=par["dt"],
        dr=par["dx"],
        fftengine="fftw",
        twosided=par["twosided"],
        prescaled=True,
        dtype="float32",
    )
    dottest(MDCpreop, par["nt2"] * par["ny"], par["nt2"] * par["nx"])
    dpre = MDCpreop * mwav.ravel()
    dpre = dpre.reshape(par["ny"], par["nt2"])
    assert_array_equal(d, dpre)

    # Apply mdd function
    minv = MDD(Gwav[:, :, par["nt"] - 1:] if par["twosided"] else Gwav,
               d[:, par["nt"] - 1:] if par["twosided"] else d,
               dt=par["dt"],
               dr=par["dx"],
               nfmax=par["nfmax"],
               twosided=par["twosided"],
               adjoint=False,
               psf=False,
               dtype="complex64",
               dottest=False,
               **dict(damp=1e-10, iter_lim=50, show=0))
    assert_array_almost_equal(mwav, minv, decimal=2)

    # Same tests for future behaviour (remove tests above in v2.0.0)
    MDCop = MDC(
        Gwav_fft.transpose(2, 0, 1),
        nt=par["nt2"],
        nv=1,
        dt=par["dt"],
        dr=par["dx"],
        twosided=par["twosided"],
        transpose=False,
        dtype="float32",
    )
    dottest(MDCop, par["nt2"] * par["ny"], par["nt2"] * par["nx"])
    mwav = mwav.T
    d = MDCop * mwav.ravel()
    d = d.reshape(par["nt2"], par["ny"])

    for it, amp in zip(it0_G, amp_G):
        ittot = it0_m + it
        if par["twosided"]:
            ittot += par["nt"] - 1
        assert (np.abs(d[ittot, par["ny"] // 2] -
                       np.abs(wav**2).sum() * amp_m * amp * par["nx"] *
                       par["dx"] * par["dt"] * np.sqrt(par["nt2"])) < 1e-2)

    minv = MDD(Gwav[:, :, par["nt"] - 1:] if par["twosided"] else Gwav,
               d[par["nt"] - 1:].T if par["twosided"] else d.T,
               dt=par["dt"],
               dr=par["dx"],
               nfmax=par["nfmax"],
               twosided=par["twosided"],
               add_negative=True,
               adjoint=False,
               psf=False,
               dtype="complex64",
               dottest=False,
               **dict(damp=1e-10, iter_lim=50, show=0))
    assert_array_almost_equal(mwav, minv.T, decimal=2)
Exemple #4
0
    def apply_multiplepoints(self,
                             trav,
                             G0=None,
                             nfft=None,
                             rtm=False,
                             greens=False,
                             dottest=False,
                             **kwargs_lsqr):
        r"""Marchenko redatuming for multiple points

        Solve the Marchenko redatuming inverse problem for multiple
        points given their direct arrival traveltime curves (``trav``)
        and waveforms (``G0``).

        Parameters
        ----------
        trav : :obj:`numpy.ndarray`
            Traveltime of first arrival from subsurface points to
            surface receivers of size :math:`[n_r \times n_{vs}]`
        G0 : :obj:`numpy.ndarray`, optional
            Direct arrival in time domain of size
            :math:`[n_r \times n_{vs} \times n_t]` (if None, create arrival
            using ``trav``)
        nfft : :obj:`int`, optional
            Number of samples in fft when creating the analytical direct wave
        rtm : :obj:`bool`, optional
            Compute and return rtm redatuming
        greens : :obj:`bool`, optional
            Compute and return Green's functions
        dottest : :obj:`bool`, optional
            Apply dot-test
        **kwargs_lsqr
            Arbitrary keyword arguments
            for :py:func:`scipy.sparse.linalg.lsqr` solver

        Returns
        ----------
        f1_inv_minus : :obj:`numpy.ndarray`
            Inverted upgoing focusing function of size
            :math:`[n_r \times n_{vs} \times n_t]`
        f1_inv_plus : :obj:`numpy.ndarray`
            Inverted downgoing focusing functionof size
            :math:`[n_r \times n_{vs} \times n_t]`
        p0_minus : :obj:`numpy.ndarray`
            Single-scattering standard redatuming upgoing Green's function
            of size :math:`[n_r \times n_{vs} \times n_t]`
        g_inv_minus : :obj:`numpy.ndarray`
            Inverted upgoing Green's function of size
            :math:`[n_r \times n_{vs} \times n_t]`
        g_inv_plus : :obj:`numpy.ndarray`
            Inverted downgoing Green's function of size
            :math:`[n_r \times n_{vs} \times n_t]`

        """
        nvs = trav.shape[1]

        # Create window
        trav_off = trav - self.toff
        trav_off = np.round(trav_off / self.dt).astype(np.int)

        w = np.zeros((self.nr, nvs, self.nt))
        for ir in range(self.nr):
            for ivs in range(nvs):
                w[ir, ivs, :trav_off[ir, ivs]] = 1
        w = np.concatenate((np.flip(w, axis=-1), w[:, :, 1:]), axis=-1)
        if self.nsmooth > 0:
            smooth = np.ones(self.nsmooth) / self.nsmooth
            w = filtfilt(smooth, 1, w)

        # Create operators
        Rop = MDC(self.Rtwosided_fft,
                  self.nt2,
                  nv=nvs,
                  dt=self.dt,
                  dr=self.dr,
                  twosided=True,
                  conj=False,
                  transpose=False,
                  prescaled=self.prescaled,
                  dtype=self.dtype)
        R1op = MDC(self.Rtwosided_fft,
                   self.nt2,
                   nv=nvs,
                   dt=self.dt,
                   dr=self.dr,
                   twosided=True,
                   conj=True,
                   transpose=False,
                   prescaled=self.prescaled,
                   dtype=self.dtype)
        Rollop = Roll(self.ns * nvs * self.nt2,
                      dims=(self.nt2, self.ns, nvs),
                      dir=0,
                      shift=-1,
                      dtype=self.dtype)
        Wop = Diagonal(w.transpose(2, 0, 1).flatten())
        Iop = Identity(self.nr * nvs * self.nt2)
        Mop = Block([[Iop, -1 * Wop * Rop], [-1 * Wop * Rollop * R1op, Iop]
                     ]) * BlockDiag([Wop, Wop])
        Gop = Block([[Iop, -1 * Rop], [-1 * Rollop * R1op, Iop]])

        if dottest:
            Dottest(Gop,
                    2 * self.nr * nvs * self.nt2,
                    2 * self.nr * nvs * self.nt2,
                    raiseerror=True,
                    verb=True)
        if dottest:
            Dottest(Mop,
                    2 * self.ns * nvs * self.nt2,
                    2 * self.nr * nvs * self.nt2,
                    raiseerror=True,
                    verb=True)

        # Create input focusing function
        if G0 is None:
            if self.wav is not None and nfft is not None:
                G0 = np.zeros((self.nr, nvs, self.nt))
                for ivs in range(nvs):
                    G0[:, ivs] = (directwave(self.wav,
                                             trav[:, ivs],
                                             self.nt,
                                             self.dt,
                                             nfft=nfft,
                                             derivative=True)).T
            else:
                logging.error('wav and/or nfft are not provided. '
                              'Provide either G0 or wav and nfft...')
                raise ValueError('wav and/or nfft are not provided. '
                                 'Provide either G0 or wav and nfft...')

        fd_plus = np.concatenate((np.flip(G0, axis=-1).transpose(2, 0, 1),
                                  np.zeros((self.nt - 1, self.nr, nvs))))

        # Run standard redatuming as benchmark
        if rtm:
            p0_minus = Rop * fd_plus.flatten()
            p0_minus = p0_minus.reshape(self.nt2, self.ns,
                                        nvs).transpose(1, 2, 0)

        # Create data and inverse focusing functions
        d = Wop * Rop * fd_plus.flatten()
        d = np.concatenate((d.reshape(self.nt2, self.ns,
                                      nvs), np.zeros(
                                          (self.nt2, self.ns, nvs))))

        # Invert for focusing functions
        f1_inv = lsqr(Mop, d.flatten(), **kwargs_lsqr)[0]
        f1_inv = f1_inv.reshape(2 * self.nt2, self.nr, nvs)
        f1_inv_tot = \
            f1_inv + np.concatenate((np.zeros((self.nt2, self.nr, nvs)),
                                     fd_plus))
        f1_inv_minus = f1_inv_tot[:self.nt2].transpose(1, 2, 0)
        f1_inv_plus = f1_inv_tot[self.nt2:].transpose(1, 2, 0)

        if greens:
            # Create Green's functions
            g_inv = Gop * f1_inv_tot.flatten()
            g_inv = np.real(g_inv)  # cast to real as Gop is a complex operator
            g_inv = g_inv.reshape(2 * self.nt2, self.ns, nvs)
            g_inv_minus = -g_inv[:self.nt2].transpose(1, 2, 0)
            g_inv_plus = np.flip(g_inv[self.nt2:], axis=0).transpose(1, 2, 0)

        if rtm and greens:
            return f1_inv_minus, f1_inv_plus, p0_minus, g_inv_minus, g_inv_plus
        elif rtm:
            return f1_inv_minus, f1_inv_plus, p0_minus
        elif greens:
            return f1_inv_minus, f1_inv_plus, g_inv_minus, g_inv_plus
        else:
            return f1_inv_minus, f1_inv_plus
Exemple #5
0
    def apply_multiplepoints(self,
                             trav,
                             G0=None,
                             nfft=None,
                             rtm=False,
                             greens=False,
                             dottest=False,
                             usematmul=False,
                             **kwargs_solver):
        r"""Marchenko redatuming for multiple points

        Solve the Marchenko redatuming inverse problem for multiple
        points given their direct arrival traveltime curves (``trav``)
        and waveforms (``G0``).

        Parameters
        ----------
        trav : :obj:`numpy.ndarray`
            Traveltime of first arrival from subsurface points to
            surface receivers of size :math:`[n_r \times n_{vs}]`
        G0 : :obj:`numpy.ndarray`, optional
            Direct arrival in time domain of size
            :math:`[n_r \times n_{vs} \times n_t]` (if None, create arrival
            using ``trav``)
        nfft : :obj:`int`, optional
            Number of samples in fft when creating the analytical direct wave
        rtm : :obj:`bool`, optional
            Compute and return rtm redatuming
        greens : :obj:`bool`, optional
            Compute and return Green's functions
        dottest : :obj:`bool`, optional
            Apply dot-test
        usematmul : :obj:`bool`, optional
            Use :func:`numpy.matmul` (``True``) or for-loop with :func:`numpy.dot`
            (``False``) in :py:class:`pylops.signalprocessing.Fredholm1` operator.
            Refer to Fredholm1 documentation for details.
        **kwargs_solver
            Arbitrary keyword arguments for chosen solver
            (:py:func:`scipy.sparse.linalg.lsqr` and
            :py:func:`pylops.optimization.solver.cgls` are used as default
            for numpy and cupy `data`, respectively)

        Returns
        ----------
        f1_inv_minus : :obj:`numpy.ndarray`
            Inverted upgoing focusing function of size
            :math:`[n_r \times n_{vs} \times n_t]`
        f1_inv_plus : :obj:`numpy.ndarray`
            Inverted downgoing focusing functionof size
            :math:`[n_r \times n_{vs} \times n_t]`
        p0_minus : :obj:`numpy.ndarray`
            Single-scattering standard redatuming upgoing Green's function
            of size :math:`[n_r \times n_{vs} \times n_t]`
        g_inv_minus : :obj:`numpy.ndarray`
            Inverted upgoing Green's function of size
            :math:`[n_r \times n_{vs} \times n_t]`
        g_inv_plus : :obj:`numpy.ndarray`
            Inverted downgoing Green's function of size
            :math:`[n_r \times n_{vs} \times n_t]`

        """
        nvs = trav.shape[1]

        # Create window
        trav_off = trav - self.toff
        trav_off = np.round(trav_off / self.dt).astype(int)

        w = np.zeros((self.nr, nvs, self.nt), dtype=self.dtype)
        for ir in range(self.nr):
            for ivs in range(nvs):
                w[ir, ivs, :trav_off[ir, ivs]] = 1
        w = np.concatenate((np.flip(w, axis=-1), w[:, :, 1:]), axis=-1)
        if self.nsmooth > 0:
            smooth = np.ones(self.nsmooth, dtype=self.dtype) / self.nsmooth
            w = filtfilt(smooth, 1, w)
        w = to_cupy_conditional(self.Rtwosided_fft, w)

        # Create operators
        Rop = MDC(
            self.Rtwosided_fft,
            self.nt2,
            nv=nvs,
            dt=self.dt,
            dr=self.dr,
            twosided=True,
            conj=False,
            fftengine=self.fftengine,
            transpose=False,
            prescaled=self.prescaled,
            usematmul=usematmul,
            dtype=self.dtype,
        )
        R1op = MDC(
            self.Rtwosided_fft,
            self.nt2,
            nv=nvs,
            dt=self.dt,
            dr=self.dr,
            twosided=True,
            conj=True,
            fftengine=self.fftengine,
            transpose=False,
            prescaled=self.prescaled,
            usematmul=usematmul,
            dtype=self.dtype,
        )
        Rollop = Roll(
            self.ns * nvs * self.nt2,
            dims=(self.nt2, self.ns, nvs),
            dir=0,
            shift=-1,
            dtype=self.dtype,
        )
        Wop = Diagonal(w.transpose(2, 0, 1).ravel())
        Iop = Identity(self.nr * nvs * self.nt2)
        Mop = Block([[Iop, -1 * Wop * Rop], [-1 * Wop * Rollop * R1op, Iop]
                     ]) * BlockDiag([Wop, Wop])
        Gop = Block([[Iop, -1 * Rop], [-1 * Rollop * R1op, Iop]])

        if dottest:
            Dottest(
                Gop,
                2 * self.nr * nvs * self.nt2,
                2 * self.nr * nvs * self.nt2,
                raiseerror=True,
                verb=True,
                backend=get_module_name(self.ncp),
            )
        if dottest:
            Dottest(
                Mop,
                2 * self.ns * nvs * self.nt2,
                2 * self.nr * nvs * self.nt2,
                raiseerror=True,
                verb=True,
                backend=get_module_name(self.ncp),
            )

        # Create input focusing function
        if G0 is None:
            if self.wav is not None and nfft is not None:
                G0 = np.zeros((self.nr, nvs, self.nt), dtype=self.dtype)
                for ivs in range(nvs):
                    G0[:, ivs] = (directwave(
                        self.wav,
                        trav[:, ivs],
                        self.nt,
                        self.dt,
                        nfft=nfft,
                        derivative=True,
                    )).T
                G0 = to_cupy_conditional(self.Rtwosided_fft, G0)
            else:
                logging.error("wav and/or nfft are not provided. "
                              "Provide either G0 or wav and nfft...")
                raise ValueError("wav and/or nfft are not provided. "
                                 "Provide either G0 or wav and nfft...")
        fd_plus = np.concatenate((
            np.flip(G0, axis=-1).transpose(2, 0, 1),
            self.ncp.zeros((self.nt - 1, self.nr, nvs), dtype=self.dtype),
        ))

        # Run standard redatuming as benchmark
        if rtm:
            p0_minus = Rop * fd_plus.ravel()
            p0_minus = p0_minus.reshape(self.nt2, self.ns,
                                        nvs).transpose(1, 2, 0)

        # Create data and inverse focusing functions
        d = Wop * Rop * fd_plus.ravel()
        d = np.concatenate((
            d.reshape(self.nt2, self.ns, nvs),
            self.ncp.zeros((self.nt2, self.ns, nvs), dtype=self.dtype),
        ))

        # Invert for focusing functions
        if self.ncp == np:
            f1_inv = lsqr(Mop, d.ravel(), **kwargs_solver)[0]
        else:
            f1_inv = cgls(Mop,
                          d.ravel(),
                          x0=self.ncp.zeros(2 * (2 * self.nt - 1) * self.nr *
                                            nvs,
                                            dtype=self.dtype),
                          **kwargs_solver)[0]

        f1_inv = f1_inv.reshape(2 * self.nt2, self.nr, nvs)
        f1_inv_tot = f1_inv + np.concatenate((self.ncp.zeros(
            (self.nt2, self.nr, nvs), dtype=self.dtype), fd_plus))
        f1_inv_minus = f1_inv_tot[:self.nt2].transpose(1, 2, 0)
        f1_inv_plus = f1_inv_tot[self.nt2:].transpose(1, 2, 0)

        if greens:
            # Create Green's functions
            g_inv = Gop * f1_inv_tot.ravel()
            g_inv = g_inv.reshape(2 * self.nt2, self.ns, nvs)
            g_inv_minus = -g_inv[:self.nt2].transpose(1, 2, 0)
            g_inv_plus = np.flip(g_inv[self.nt2:], axis=0).transpose(1, 2, 0)

        if rtm and greens:
            return f1_inv_minus, f1_inv_plus, p0_minus, g_inv_minus, g_inv_plus
        elif rtm:
            return f1_inv_minus, f1_inv_plus, p0_minus
        elif greens:
            return f1_inv_minus, f1_inv_plus, g_inv_minus, g_inv_plus
        else:
            return f1_inv_minus, f1_inv_plus
def test_MDC_Nvirtualsources(par):
    """Dot-test and inversion for MDC operator of N virtual source
    """
    if par['twosided']:
        par['nt2'] = 2 * par['nt'] - 1
    else:
        par['nt2'] = par['nt']
    v = 1500
    it0_m = 25
    t0_m = it0_m * par['dt']
    theta_m = 0
    phi_m = 0
    amp_m = 1.

    it0_G = np.array([25, 50, 75])
    t0_G = it0_G * par['dt']
    theta_G = (0, 0, 0)
    phi_G = (0, 0, 0)
    amp_G = (1., 0.6, 2.)

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

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

    # Generate model
    _, mwav = linear3d(x, x, t, v, t0_m, theta_m, phi_m, amp_m, wav)

    # Generate operator
    _, Gwav = linear3d(x, y, t, v, t0_G, theta_G, phi_G, amp_G, wav)

    # Add negative part to data and model
    if par['twosided']:
        mwav = np.concatenate((np.zeros(
            (par['nx'], par['nx'], par['nt'] - 1)), mwav),
                              axis=-1)
        Gwav = np.concatenate((np.zeros(
            (par['ny'], par['nx'], par['nt'] - 1)), Gwav),
                              axis=-1)

    # Define MDC linear operator
    Gwav_fft = np.fft.fft(Gwav, par['nt2'], axis=-1)
    Gwav_fft = Gwav_fft[..., :par['nfmax']]

    MDCop = MDC(Gwav_fft,
                nt=par['nt2'],
                nv=par['nx'],
                dt=par['dt'],
                dr=par['dx'],
                twosided=par['twosided'],
                dtype='float32')
    dottest(MDCop, par['nt2'] * par['ny'] * par['nx'],
            par['nt2'] * par['nx'] * par['nx'])

    # Create data
    d = MDCop * mwav.flatten()
    d = d.reshape(par['ny'], par['nx'], par['nt2'])

    # Check that events are at correct time
    for it, amp in zip(it0_G, amp_G):
        ittot = it0_m + it
        if par['twosided']:
            ittot += par['nt'] - 1
        assert d[par['ny'] // 2, par['nx'] // 2, ittot] > \
               d[par['ny'] // 2, par['nx'] // 2, ittot - 1]
        assert d[par['ny'] // 2, par['nx'] // 2, ittot] > \
               d[par['ny'] // 2, par['nx'] // 2, ittot + 1]

    # Apply mdd function
    minv = MDD(Gwav[:, :, par['nt'] - 1:] if par['twosided'] else Gwav,
               d[:, :, par['nt'] - 1:] if par['twosided'] else d,
               dt=par['dt'],
               dr=par['dx'],
               nfmax=par['nfmax'],
               twosided=par['twosided'],
               adjoint=False,
               psf=False,
               dtype='complex64',
               dottest=False,
               **dict(damp=1e-10, iter_lim=50, show=1))
    assert_array_almost_equal(mwav, minv, decimal=2)

    # Same tests for future behaviour (remove tests above in v2.0.0)
    MDCop = MDC(Gwav_fft.transpose(2, 0, 1),
                nt=par['nt2'],
                nv=par['nx'],
                dt=par['dt'],
                dr=par['dx'],
                twosided=par['twosided'],
                transpose=False,
                dtype='float32')
    dottest(MDCop, par['nt2'] * par['ny'] * par['nx'],
            par['nt2'] * par['nx'] * par['nx'])

    mwav = mwav.transpose(2, 0, 1)
    d = MDCop * mwav.flatten()
    d = d.reshape(par['nt2'], par['ny'], par['nx'])

    for it, amp in zip(it0_G, amp_G):
        ittot = it0_m + it
        if par['twosided']:
            ittot += par['nt'] - 1
        assert d[ittot, par['ny'] // 2, par['nx'] // 2] > \
               d[ittot - 1, par['ny'] // 2, par['nx'] // 2]
        assert d[ittot, par['ny'] // 2, par['nx'] // 2] > \
               d[ittot + 1, par['ny'] // 2, par['nx'] // 2]

    minv = MDD(
        Gwav[:, :, par['nt'] - 1:] if par['twosided'] else Gwav,
        d[par['nt'] -
          1:].transpose(1, 2, 0) if par['twosided'] else d.transpose(1, 2, 0),
        dt=par['dt'],
        dr=par['dx'],
        nfmax=par['nfmax'],
        twosided=par['twosided'],
        add_negative=True,
        adjoint=False,
        psf=False,
        dtype='complex64',
        dottest=False,
        **dict(damp=1e-10, iter_lim=50, show=1))
    assert_array_almost_equal(mwav, minv.transpose(2, 0, 1), decimal=2)
Exemple #7
0
    def apply_onepoint(self,
                       trav,
                       G0=None,
                       nfft=None,
                       rtm=False,
                       greens=False,
                       dottest=False,
                       fast=True,
                       **kwargs_lsqr):
        r"""Marchenko redatuming for one point

        Solve the Marchenko redatuming inverse problem for a single point given its
        direct arrival traveltime curve (``trav``) and waveform (``G0``).

        Parameters
        ----------
        trav : :obj:`numpy.ndarray`
            Traveltime of first arrival from subsurface point to surface receivers
            of size :math:`[n_r \times 1]`
        G0 : :obj:`numpy.ndarray`, optional
            Direct arrival in time domain of size :math:`[n_r \times n_t]`
            (if None, create arrival using ``trav``)
        nfft : :obj:`int`, optional
            Number of samples in fft when creating the analytical direct wave
        rtm : :obj:`bool`, optional
            Compute and return rtm redatuming
        greens : :obj:`bool`, optional
            Compute and return Green's functions
        dottest : :obj:`bool`, optional
            Apply dot-test
        fast : :obj:`bool`
            Fast application of MDC when model has only one virtual source
            (``True``) or not (``False``)
        **kwargs_lsqr
            Arbitrary keyword arguments for :py:func:`scipy.sparse.linalg.lsqr` solver

        Returns
        ----------
        f1_inv_minus : :obj:`numpy.ndarray`
            Inverted upgoing focusing function of size :math:`[n_r \times n_t]`
        f1_inv_plus : :obj:`numpy.ndarray`
            Inverted downgoing focusing function of size :math:`[n_r \times n_t]`
        p0_minus : :obj:`numpy.ndarray`
            Single-scattering standard redatuming upgoing Green's function of
            size :math:`[n_r \times n_t]`
        g_inv_minus : :obj:`numpy.ndarray`
            Inverted upgoing Green's function of size :math:`[n_r \times n_t]`
        g_inv_plus : :obj:`numpy.ndarray`
            Inverted downgoing Green's function of size :math:`[n_r \times n_t]`

        """
        # Create window
        trav_off = trav - self.toff
        trav_off = np.round(trav_off / self.dt).astype(np.int)

        # window
        w = np.zeros((self.nr, self.nt))
        for ir in range(self.nr):
            w[ir, :trav_off[ir]] = 1
        w = np.hstack((np.fliplr(w), w[:, 1:]))
        if self.nsmooth > 0:
            smooth = np.ones(self.nsmooth) / self.nsmooth
            w = filtfilt(smooth, 1, w)

        # Create operators
        Rop = MDC(self.Rtwosided_fft,
                  self.nt2,
                  nv=1,
                  dt=self.dt,
                  dr=self.dr,
                  twosided=True,
                  fast=fast,
                  dtype=self.dtype)
        R1op = MDC(self.R1twosided_fft,
                   self.nt2,
                   nv=1,
                   dt=self.dt,
                   dr=self.dr,
                   twosided=True,
                   fast=fast,
                   dtype=self.dtype)

        Wop = Diagonal(w.flatten())
        Iop = Identity(self.nr * (2 * self.nt - 1))
        Mop = VStack(
            [HStack([Iop, -1 * Wop * Rop]),
             HStack([-1 * Wop * R1op, Iop])]) * BlockDiag([Wop, Wop])
        Gop = VStack([HStack([Iop, -1 * Rop]), HStack([-1 * R1op, Iop])])

        if dottest:
            assert Dottest(Gop,
                           2 * self.nr * self.nt2,
                           2 * self.nr * self.nt2,
                           verb=True)
        if dottest:
            assert Dottest(Mop,
                           2 * self.nr * self.nt2,
                           2 * self.nr * self.nt2,
                           verb=True)

        # Create input focusing function
        if G0 is None:
            if self.wav is not None and nfft is not None:
                G0 = (directwave(self.wav, trav, self.nt, self.dt,
                                 nfft=nfft)).T
            else:
                logging.error('wav and/or nfft are not provided. '
                              'Provide either G0 or wav and nfft...')
                raise ValueError('wav and/or nfft are not provided. '
                                 'Provide either G0 or wav and nfft...')

        fd_plus = np.concatenate(
            (np.fliplr(G0), np.zeros((self.nr, self.nt - 1))), axis=-1)

        # Run standard redatuming as benchmark
        if rtm:
            p0_minus = Rop * fd_plus.flatten()
            p0_minus = p0_minus.reshape(self.nr, self.nt2)

        # Create data and inverse focusing functions
        d = Wop * Rop * fd_plus.flatten()
        d = np.concatenate(
            (d.reshape(self.nr, self.nt2), np.zeros((self.nr, self.nt2))))

        f1_inv = lsqr(Mop, d.flatten(), **kwargs_lsqr)[0]
        f1_inv = f1_inv.reshape(2 * self.nr, self.nt2)

        f1_inv_tot = f1_inv + np.concatenate((np.zeros(
            (self.nr, self.nt2)), fd_plus))
        f1_inv_minus, f1_inv_plus = f1_inv_tot[:self.nr], f1_inv_tot[self.nr:]

        if greens:
            # Create Green's functions
            g_inv = Gop * f1_inv_tot.flatten()
            g_inv = g_inv.reshape(2 * self.nr, (2 * self.nt - 1))
            g_inv_minus, g_inv_plus = -g_inv[:self.nr], np.fliplr(
                g_inv[self.nr:])

        if rtm and greens:
            return f1_inv_minus, f1_inv_plus, p0_minus, g_inv_minus, g_inv_plus
        elif rtm:
            return f1_inv_minus, f1_inv_plus, p0_minus
        elif greens:
            return f1_inv_minus, f1_inv_plus, g_inv_minus, g_inv_plus
        else:
            return f1_inv_minus, f1_inv_plus
def test_MDC_1virtualsource(par):
    """Dot-test and comparison with pylops for MDC operator of 1 virtual source
    """
    if par['twosided']:
        par['nt2'] = 2 * par['nt'] - 1
    else:
        par['nt2'] = par['nt']
    v = 1500
    it0_m = 25
    t0_m = it0_m * par['dt']
    theta_m = 0
    amp_m = 1.

    it0_G = np.array([25, 50, 75])
    t0_G = it0_G * par['dt']
    theta_G = (0, 0, 0)
    phi_G = (0, 0, 0)
    amp_G = (1., 0.6, 2.)

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

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

    # Generate model
    _, mwav = linear2d(x, t, v, t0_m, theta_m, amp_m, wav)
    # Generate operator
    _, Gwav = linear3d(x, y, t, v, t0_G, theta_G, phi_G, amp_G, wav)

    # Add negative part to data and model
    if par['twosided']:
        mwav = np.concatenate((np.zeros((par['nx'], par['nt'] - 1)), mwav),
                              axis=-1)
        Gwav = np.concatenate((np.zeros(
            (par['ny'], par['nx'], par['nt'] - 1)), Gwav),
                              axis=-1)

    # Define MDC linear operator
    Gwav_fft = np.fft.fft(Gwav, par['nt2'], axis=-1)
    Gwav_fft = Gwav_fft[..., :par['nfmax']]
    dGwav_fft = da.from_array(Gwav_fft.transpose(2, 0, 1))

    dMDCop = dMDC(dGwav_fft,
                  nt=par['nt2'],
                  nv=1,
                  dt=par['dt'],
                  dr=par['dx'],
                  twosided=par['twosided'])
    MDCop = MDC(Gwav_fft.transpose(2, 0, 1),
                nt=par['nt2'],
                nv=1,
                dt=par['dt'],
                dr=par['dx'],
                twosided=par['twosided'],
                transpose=False,
                dtype='float32')
    dottest(dMDCop,
            par['nt2'] * par['ny'],
            par['nt2'] * par['nx'],
            chunks=((par['nt2'] * par['ny'], par['nt2'] * par['nx'])))

    # Compare results with pylops implementation
    mwav = mwav.T
    dy = dMDCop * da.from_array(mwav.flatten())
    y = MDCop * mwav.flatten()
    assert_array_almost_equal(dy.compute(), y, decimal=5)

    # Apply mdd function
    dy = dy.reshape(par['nt2'], par['ny'])
    print(dy)
    minv = MDD(dGwav_fft,
               dy[par['nt'] - 1:] if par['twosided'] else dy,
               dt=par['dt'],
               dr=par['dx'],
               nfmax=par['nfmax'],
               twosided=par['twosided'],
               adjoint=False,
               dottest=False,
               **dict(niter=50))
    print('minv', minv)
    assert_array_almost_equal(mwav, minv.compute(), decimal=2)
Exemple #9
0
    def apply_onepoint(self,
                       trav,
                       G0=None,
                       nfft=None,
                       rtm=False,
                       greens=False,
                       dottest=False,
                       fast=None,
                       **kwargs_solver):
        r"""Marchenko redatuming for one point

        Solve the Marchenko redatuming inverse problem for a single point
        given its direct arrival traveltime curve (``trav``)
        and waveform (``G0``).

        Parameters
        ----------
        trav : :obj:`numpy.ndarray`
            Traveltime of first arrival from subsurface point to
            surface receivers of size :math:`[n_r \times 1]`
        G0 : :obj:`numpy.ndarray`, optional
            Direct arrival in time domain of size :math:`[n_r \times n_t]`
            (if None, create arrival using ``trav``)
        nfft : :obj:`int`, optional
            Number of samples in fft when creating the analytical direct wave
        rtm : :obj:`bool`, optional
            Compute and return rtm redatuming
        greens : :obj:`bool`, optional
            Compute and return Green's functions
        dottest : :obj:`bool`, optional
            Apply dot-test
        fast : :obj:`bool`
            *Deprecated*, will be removed in v2.0.0
        **kwargs_solver
            Arbitrary keyword arguments for chosen solver
            (:py:func:`scipy.sparse.linalg.lsqr` and
            :py:func:`pylops.optimization.solver.cgls` are used as default
            for numpy and cupy `data`, respectively)

        Returns
        ----------
        f1_inv_minus : :obj:`numpy.ndarray`
            Inverted upgoing focusing function of size :math:`[n_r \times n_t]`
        f1_inv_plus : :obj:`numpy.ndarray`
            Inverted downgoing focusing function
            of size :math:`[n_r \times n_t]`
        p0_minus : :obj:`numpy.ndarray`
            Single-scattering standard redatuming upgoing Green's function of
            size :math:`[n_r \times n_t]`
        g_inv_minus : :obj:`numpy.ndarray`
            Inverted upgoing Green's function of size :math:`[n_r \times n_t]`
        g_inv_plus : :obj:`numpy.ndarray`
            Inverted downgoing Green's function
            of size :math:`[n_r \times n_t]`

        """
        # Create window
        trav_off = trav - self.toff
        trav_off = np.round(trav_off / self.dt).astype(np.int)

        w = np.zeros((self.nr, self.nt), dtype=self.dtype)
        for ir in range(self.nr):
            w[ir, :trav_off[ir]] = 1
        w = np.hstack((np.fliplr(w), w[:, 1:]))
        if self.nsmooth > 0:
            smooth = np.ones(self.nsmooth, dtype=self.dtype) / self.nsmooth
            w = filtfilt(smooth, 1, w)
        w = to_cupy_conditional(self.Rtwosided_fft, w)

        # Create operators
        Rop = MDC(self.Rtwosided_fft,
                  self.nt2,
                  nv=1,
                  dt=self.dt,
                  dr=self.dr,
                  twosided=True,
                  conj=False,
                  transpose=False,
                  saveGt=self.saveRt,
                  prescaled=self.prescaled,
                  dtype=self.dtype)
        R1op = MDC(self.Rtwosided_fft,
                   self.nt2,
                   nv=1,
                   dt=self.dt,
                   dr=self.dr,
                   twosided=True,
                   conj=True,
                   transpose=False,
                   saveGt=self.saveRt,
                   prescaled=self.prescaled,
                   dtype=self.dtype)
        Rollop = Roll(self.nt2 * self.ns,
                      dims=(self.nt2, self.ns),
                      dir=0,
                      shift=-1,
                      dtype=self.dtype)
        Wop = Diagonal(w.T.flatten())
        Iop = Identity(self.nr * self.nt2)
        Mop = Block([[Iop, -1 * Wop * Rop], [-1 * Wop * Rollop * R1op, Iop]
                     ]) * BlockDiag([Wop, Wop])
        Gop = Block([[Iop, -1 * Rop], [-1 * Rollop * R1op, Iop]])

        if dottest:
            Dottest(Gop,
                    2 * self.ns * self.nt2,
                    2 * self.nr * self.nt2,
                    raiseerror=True,
                    verb=True,
                    backend=get_module_name(self.ncp))
        if dottest:
            Dottest(Mop,
                    2 * self.ns * self.nt2,
                    2 * self.nr * self.nt2,
                    raiseerror=True,
                    verb=True,
                    backend=get_module_name(self.ncp))

        # Create input focusing function
        if G0 is None:
            if self.wav is not None and nfft is not None:
                G0 = (directwave(self.wav,
                                 trav,
                                 self.nt,
                                 self.dt,
                                 nfft=nfft,
                                 derivative=True)).T
                G0 = to_cupy_conditional(self.Rtwosided_fft, G0)
            else:
                logging.error('wav and/or nfft are not provided. '
                              'Provide either G0 or wav and nfft...')
                raise ValueError('wav and/or nfft are not provided. '
                                 'Provide either G0 or wav and nfft...')
        fd_plus = np.concatenate((np.fliplr(G0).T,
                                  self.ncp.zeros((self.nt - 1, self.nr),
                                                 dtype=self.dtype)))

        # Run standard redatuming as benchmark
        if rtm:
            p0_minus = Rop * fd_plus.flatten()
            p0_minus = p0_minus.reshape(self.nt2, self.ns).T

        # Create data and inverse focusing functions
        d = Wop * Rop * fd_plus.flatten()
        d = np.concatenate((d.reshape(self.nt2, self.ns),
                            self.ncp.zeros((self.nt2, self.ns), self.dtype)))

        # Invert for focusing functions
        if self.ncp == np:
            f1_inv = lsqr(Mop, d.flatten(), **kwargs_solver)[0]
        else:
            f1_inv = cgls(Mop,
                          d.flatten(),
                          x0=self.ncp.zeros(2 * (2 * self.nt - 1) * self.nr,
                                            dtype=self.dtype),
                          **kwargs_solver)[0]

        f1_inv = f1_inv.reshape(2 * self.nt2, self.nr)
        f1_inv_tot = f1_inv + np.concatenate((self.ncp.zeros(
            (self.nt2, self.nr), dtype=self.dtype), fd_plus))
        f1_inv_minus = f1_inv_tot[:self.nt2].T
        f1_inv_plus = f1_inv_tot[self.nt2:].T
        if greens:
            # Create Green's functions
            g_inv = Gop * f1_inv_tot.flatten()
            g_inv = g_inv.reshape(2 * self.nt2, self.ns)
            g_inv_minus, g_inv_plus = -g_inv[:self.nt2].T, \
                                      np.fliplr(g_inv[self.nt2:].T)
        if rtm and greens:
            return f1_inv_minus, f1_inv_plus, p0_minus, g_inv_minus, g_inv_plus
        elif rtm:
            return f1_inv_minus, f1_inv_plus, p0_minus
        elif greens:
            return f1_inv_minus, f1_inv_plus, g_inv_minus, g_inv_plus
        else:
            return f1_inv_minus, f1_inv_plus