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