def test_direct2D():
    """Check consistency of analytical 2D Green's function with FD modelling
    """
    inputdata = np.load(inputfile2d)

    # Receivers
    r = inputdata['r']
    nr = r.shape[1]

    # Virtual points
    vs = inputdata['vs']

    # Time axis
    t = inputdata['t']
    dt, nt = t[1] - t[0], len(t)

    # FD GF
    G0FD = inputdata['G0sub']
    wav = inputdata['wav']
    wav_c = np.argmax(wav)

    G0FD = np.apply_along_axis(convolve, 0, G0FD, wav, mode='full')
    G0FD = G0FD[wav_c:][:nt]

    # Analytic GF
    trav = np.sqrt((vs[0] - r[0]) ** 2 + (vs[1] - r[1]) ** 2) / vel
    G0ana = directwave(wav, trav, nt, dt, nfft=nt, derivative=False)

    # Differentiate to get same response as in FD modelling
    G0ana = np.diff(G0ana, axis=0)
    G0ana = np.vstack([G0ana, np.zeros(nr)])

    assert_array_almost_equal(G0FD / np.max(np.abs(G0FD)),
                              G0ana / np.max(np.abs(G0ana)), decimal=1)
Example #2
0
def test_direct3D():
    """Check consistency of analytical 3D Green's function with FD modelling"""
    inputdata = np.load(inputfile3d)

    # Receivers
    r = inputdata["r"]
    nr = r.shape[0]

    # Virtual points
    vs = inputdata["vs"]

    # Time axis
    t = inputdata["t"]
    dt, nt = t[1] - t[0], len(t)

    # FD GF
    G0FD = inputdata["G0"][:, :nr]
    wav = inputdata["wav"]
    wav_c = np.argmax(wav)

    G0FD = np.apply_along_axis(convolve, 0, G0FD, wav, mode="full")
    G0FD = G0FD[wav_c:][:nt]

    # Analytic GF
    dist = np.sqrt((vs[0] - r[:, 0])**2 + (vs[1] - r[:, 1])**2 +
                   (vs[2] - r[:, 2])**2)
    trav = dist / vel
    G0ana = directwave(wav,
                       trav,
                       nt,
                       dt,
                       nfft=nt,
                       dist=dist,
                       kind="3d",
                       derivative=False)

    # Differentiate to get same response as in FD modelling
    G0ana = np.diff(G0ana, axis=0)
    G0ana = np.vstack([G0ana, np.zeros(nr)])

    assert_array_almost_equal(G0FD / np.max(np.abs(G0FD)),
                              G0ana / np.max(np.abs(G0ana)),
                              decimal=1)
Example #3
0
    def apply_multiplepoints(self, trav, dist=None, G0=None, nfft=None,
                             rtm=False, greens=False,
                             dottest=False, **kwargs_cgls):
        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}]`
        dist: :obj:`numpy.ndarray`, optional
            Distance between subsurface point to
            surface receivers of size :math:`[n_r \times n_{vs}]`
            (if provided the analytical direct arrival will be computed using
            a 3d formulation)
        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_cgls
            Arbitrary keyword arguments for
            :py:func:`pylops_distributed.optimization.cg.cgls` 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, saveGt=self.saveRt)
        R1op = MDC(self.Rtwosided_fft, self.nt2, nv=nvs, dt=self.dt,
                   dr=self.dr, twosided=True, conj=True, saveGt=self.saveRt)
        Rollop = Roll(self.ns * nvs * self.nt2,
                      dims=(self.nt2, self.ns, nvs),
                      dir=0, shift=-1, dtype=self.dtype)
        Wop = Diagonal(da.from_array(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,
                    chunks=(2 * self.ns * 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,
                    chunks=(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)).T
                                             # dist=dist,
                                             # kind='2d' if dist is None else '3d')).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))))
        fd_plus = da.from_array(fd_plus).rechunk(fd_plus.shape)

        # 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 = da.concatenate((d.reshape(self.nt2, self.ns, nvs),
                            da.zeros((self.nt2, self.ns, nvs))))

        # Invert for focusing functions
        f1_inv = cgls(Mop, d.flatten(), **kwargs_cgls)[0]
        f1_inv = f1_inv.reshape(2 * self.nt2, self.nr, nvs)
        f1_inv_tot = \
            f1_inv + da.concatenate((np.zeros((self.nt2, self.nr, nvs)),
                                     fd_plus))
        if greens:
            # Create Green's functions
            g_inv = Gop * f1_inv_tot.flatten()
            g_inv = g_inv.reshape(2 * self.nt2, self.ns, nvs)

        # Compute
        if rtm and greens:
            d, p0_minus, f1_inv_tot, g_inv = \
                da.compute(d, p0_minus, f1_inv_tot, g_inv)
        elif rtm:
            d, p0_minus, f1_inv_tot = \
                da.compute(d, p0_minus, f1_inv_tot)
        elif greens:
            d, f1_inv_tot, g_inv = \
                da.compute(d, f1_inv_tot, g_inv)
        else:
            d, f1_inv_tot = \
                da.compute(d, f1_inv_tot)

        # Separate focusing and Green's functions
        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:
            g_inv = np.real(g_inv)  # cast to real as Gop is a complex operator
            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