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)
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)
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