def MDD(G, d, dt=0.004, dr=1., nfmax=None, wav=None, twosided=True, causality_precond=False, adjoint=False, psf=False, dtype='float64', dottest=False, saveGt=True, add_negative=True, **kwargs_lsqr): r"""Multi-dimensional deconvolution. Solve multi-dimensional deconvolution problem using :py:func:`scipy.sparse.linalg.lsqr` iterative solver. Parameters ---------- G : :obj:`numpy.ndarray` Multi-dimensional convolution kernel in time domain of size :math:`[n_s \times n_r \times n_t]` for ``twosided=False`` or ``twosided=True`` and ``add_negative=True`` (with only positive times) or size :math:`[n_s \times n_r \times 2*n_t-1]` for ``twosided=True`` and ``add_negative=False`` (with both positive and negative times) d : :obj:`numpy.ndarray` Data in time domain :math:`[n_s (\times n_vs) \times n_t]` dt : :obj:`float`, optional Sampling of time integration axis dr : :obj:`float`, optional Sampling of receiver integration axis nfmax : :obj:`int`, optional Index of max frequency to include in deconvolution process wav : :obj:`numpy.ndarray`, optional Wavelet to convolve to the inverted model and psf. If ``None``, the outputs of the inversion are returned directly twosided : :obj:`bool`, optional MDC operator and data both negative and positive time (``True``) or only positive (``False``) add_negative : :obj:`bool`, optional Add negative side to MDC operator and data (``True``) or not (``False``)- operator and data are already provided with both positive and negative sides. To be used only with ``twosided=True``. causality_precond : :obj:`bool`, optional Apply causality mask (``True``) or not (``False``) adjoint : :obj:`bool`, optional Compute and return adjoint(s) psf : :obj:`bool`, optional Compute and return Point Spread Function (PSF) and its inverse dtype : :obj:`bool`, optional Type of elements in input array. dottest : :obj:`bool`, optional Apply dot-test saveGt : :obj:`bool`, optional Save ``G`` and ``G^H`` to speed up the computation of adjoint of :class:`pylops.signalprocessing.Fredholm1` (``True``) or create ``G^H`` on-the-fly (``False``) Note that ``saveGt=True`` will be faster but double the amount of required memory **kwargs_lsqr Arbitrary keyword arguments for :py:func:`scipy.sparse.linalg.lsqr` solver Returns ------- minv : :obj:`numpy.ndarray` Inverted model of size :math:`[n_r (\times n_{vs}) \times n_t]` for ``twosided=False`` or :math:`[n_r (\times n_vs) \times 2*n_t-1]` for ``twosided=True`` madj : :obj:`numpy.ndarray` Adjoint model of size :math:`[n_r (\times n_{vs}) \times n_t]` for ``twosided=False`` or :math:`[n_r (\times n_r) \times 2*n_t-1]` for ``twosided=True`` psfinv : :obj:`numpy.ndarray` Inverted psf of size :math:`[n_r \times n_r \times n_t]` for ``twosided=False`` or :math:`[n_r \times n_r \times 2*n_t-1]` for ``twosided=True`` psfadj : :obj:`numpy.ndarray` Adjoint psf of size :math:`[n_r \times n_r \times n_t]` for ``twosided=False`` or :math:`[n_r \times n_r \times 2*n_t-1]` for ``twosided=True`` See Also -------- MDC : Multi-dimensional convolution Notes ----- Multi-dimensional deconvolution (MDD) is a mathematical ill-solved problem, well-known in the image processing and geophysical community [1]_. MDD aims at removing the effects of a Multi-dimensional Convolution (MDC) kernel or the so-called blurring operator or point-spread function (PSF) from a given data. It can be written as .. math:: \mathbf{d}= \mathbf{D} \mathbf{m} or, equivalently, by means of its normal equation .. math:: \mathbf{m}= (\mathbf{D}^H\mathbf{D})^{-1} \mathbf{D}^H\mathbf{d} where :math:`\mathbf{D}^H\mathbf{D}` is the PSF. .. [1] Wapenaar, K., van der Neut, J., Ruigrok, E., Draganov, D., Hunziker, J., Slob, E., Thorbecke, J., and Snieder, R., "Seismic interferometry by crosscorrelation and by multi-dimensional deconvolution: a systematic comparison", Geophyscial Journal International, vol. 185, pp. 1335-1364. 2011. """ ns, nr, nt = G.shape if len(d.shape) == 2: ns, nt = d.shape nv = 1 else: ns, nv, nt = d.shape if twosided: if add_negative: nt2 = 2 * nt - 1 else: nt2 = nt nt = (nt2 + 1) // 2 nfmax_allowed = int(np.ceil((nt2 + 1) / 2)) else: nt2 = nt nfmax_allowed = nt # Fix nfmax to be at maximum equal to half of the size of fft samples if nfmax is None or nfmax > nfmax_allowed: nfmax = nfmax_allowed logging.warning('nfmax set equal to ceil[(nt+1)/2=%d]' % nfmax) # Add negative part to data and model if twosided and add_negative: G = np.concatenate((np.zeros((ns, nr, nt - 1)), G), axis=-1) d = np.concatenate((np.squeeze(np.zeros((ns, nv, nt - 1))), d), axis=-1) # Bring kernel to frequency domain Gfft = np.fft.rfft(G, nt2, axis=-1) Gfft = Gfft[..., :nfmax] # Bring frequency/time to first dimension Gfft = np.moveaxis(Gfft, -1, 0) d = np.moveaxis(d, -1, 0) if psf: G = np.moveaxis(G, -1, 0) # Define MDC linear operator MDCop = MDC(Gfft, nt2, nv=nv, dt=dt, dr=dr, twosided=twosided, transpose=False, saveGt=saveGt) if psf: PSFop = MDC(Gfft, nt2, nv=nr, dt=dt, dr=dr, twosided=twosided, transpose=False, saveGt=saveGt) if dottest: Dottest(MDCop, nt2 * ns * nv, nt2 * nr * nv, verb=True) if psf: Dottest(PSFop, nt2 * ns * nr, nt2 * nr * nr, verb=True) # Adjoint if adjoint: madj = MDCop.H * d.flatten() madj = np.squeeze(madj.reshape(nt2, nr, nv)) madj = np.moveaxis(madj, 0, -1) if psf: psfadj = PSFop.H * G.flatten() psfadj = np.squeeze(psfadj.reshape(nt2, nr, nr)) psfadj = np.moveaxis(psfadj, 0, -1) # Inverse if twosided and causality_precond: P = np.ones((nt2, nr, nv)) P[:nt - 1] = 0 Pop = Diagonal(P) minv = PreconditionedInversion(MDCop, Pop, d.flatten(), returninfo=False, **kwargs_lsqr) else: minv = lsqr(MDCop, d.flatten(), **kwargs_lsqr)[0] minv = np.squeeze(minv.reshape(nt2, nr, nv)) minv = np.moveaxis(minv, 0, -1) if wav is not None: minv = sp_convolve1d(minv, wav, axis=-1) if psf: psfinv = lsqr(PSFop, G.flatten(), **kwargs_lsqr)[0] psfinv = np.squeeze(psfinv.reshape(nt2, nr, nr)) psfinv = np.moveaxis(psfinv, 0, -1) if wav is not None: psfinv = sp_convolve1d(psfinv, wav, axis=-1) if adjoint and psf: return minv, madj, psfinv, psfadj elif adjoint: return minv, madj elif psf: return minv, psfinv else: return minv
def MDD(G, d, dt=0.004, dr=1., nfmax=None, wav=None, twosided=True, causality_precond=False, adjoint=False, psf=False, dtype='complex64', dottest=False, **kwargs_lsqr): r"""Multi-dimensional deconvolution. Solve multi-dimensional deconvolution problem using :py:func:`scipy.sparse.linalg.lsqr` iterative solver. Parameters ---------- G : :obj:`numpy.ndarray` Multi-dimensional convolution kernel in frequency domain of size :math:`[n_s \times n_r \times n_{fmax}]` d : :obj:`numpy.ndarray` Data in time domain :math:`[ns (\times nr) \times nt]` dt : :obj:`float`, optional Sampling of time integration axis dr : :obj:`float`, optional Sampling of receiver integration axis nfmax : :obj:`int`, optional Index of max frequency to include in deconvolution process twosided : :obj:`bool`, optional MDC operator has both negative and positive time (``True``) or only positive (``False``) causality_precond : :obj:`bool`, optional Type of elements in input array. adjoint : :obj:`bool`, optional Compute and return adjoint(s) psf : :obj:`bool`, optional Compute and return Point Spread Function (PSF) and its inverse dtype : :obj:`bool`, optional Type of elements in input array. dottest : :obj:`bool`, optional Apply dot-test **kwargs_lsqr Arbitrary keyword arguments for :py:func:`scipy.sparse.linalg.lsqr` solver Returns ---------- minv: :obj:`numpy.ndarray` Inverted model. madj: :obj:`numpy.ndarray` Adjoint model. psfinv: :obj:`numpy.ndarray` Inverted psf. psfadj: :obj:`numpy.ndarray` Adjoint psf. See Also -------- MDC : Multi-dimensional convolution Notes ----- Multi-dimensional deconvolution (MDD) is a mathematical ill-solved problem, well-known in the image processing and geophysical community [1]_. MDD aims at removing the effects of a Multi-dimensional Convolution (MDC) kernel or the so-called blurring operator or point-spread function (PSF) from a given data. It can be written as .. math:: \mathbf{d}= \mathbf{D} \mathbf{m} or, equivalently, by means of its normal equation .. math:: \mathbf{m}= (\mathbf{D}^H\mathbf{D})^{-1} \mathbf{D}^H\mathbf{d}$$ where :math:`\mathbf{D}^H\mathbf{D}` is the PSF. .. [1] Wapenaar, K., van der Neut, J., Ruigrok, E., Draganov, D., Hunziker, J., Slob, E., Thorbecke, J., and Snieder, R., "Seismic interferometry by crosscorrelation and by multi-dimensional deconvolution: a systematic comparison", Geophyscial Journal International, vol. 185, pp. 1335-1364. 2011. """ ns, nr, nt = G.shape if len(d.shape) == 2: ns, nt = d.shape nv = 1 else: ns, nv, nt = d.shape nt2 = nt if twosided == False else 2 * nt - 1 # Fix nfmax to be at maximum equal to half of the size of fft samples if nfmax == None or nfmax > np.ceil((nt2 + 1) / 2): nfmax = int(np.ceil((nt2+1)/2)) logging.warning('nfmax set equal to (nt+1)/2=%d' % nfmax) # Add negative part to data and model if twosided: G = np.concatenate((np.zeros((ns, nr, nt - 1)), G), axis=-1) d = np.concatenate((np.squeeze(np.zeros((ns, nv, nt - 1))), d), axis=-1) # Define MDC linear operator Gfft = np.fft.rfft(G, nt2, axis=-1) Gfft = Gfft[..., :nfmax] MDCop = MDC(Gfft, nt2, nv=nv, dt=dt, dr=dr, twosided=twosided, dtype=dtype) if psf: PSFop = MDC(Gfft, nt2, nv=nr, dt=dt, dr=dr, twosided=twosided, dtype=dtype) if dottest: Dottest(MDCop, nt2*ns*nv, nt2*nr*nv, verb=True) if psf: Dottest(PSFop, nt2 * ns * nr, nt2 * nr * nr, verb=True) # Adjoint if adjoint: madj = MDCop.H * d.flatten() madj = np.squeeze(madj.reshape(nr, nv, nt2)) if psf: psfadj = PSFop.H * G.flatten() psfadj = np.squeeze(psfadj.reshape(nr, nr, nt2)) # Inverse if twosided and causality_precond: P = np.ones((nr, nv, nt2)) P[:, :, :nt - 1] = 0 Pop = Diagonal(P) minv = PreconditionedInversion(MDCop, Pop, d.flatten(), returninfo=False, **kwargs_lsqr) else: minv = lsqr(MDCop, d.flatten(), **kwargs_lsqr)[0] minv = np.squeeze(minv.reshape(nr, nv, nt2)) if wav is not None: minv = sp_convolve1d(minv, wav, axis=-1) if psf: psfinv = lsqr(PSFop, G.flatten(), **kwargs_lsqr)[0] psfinv = np.squeeze(psfinv.reshape(nr, nr, nt2)) if wav is not None: psfinv = sp_convolve1d(psfinv, wav, axis=-1) if adjoint and psf: return minv, madj, psfinv, psfadj elif adjoint: return minv, madj elif psf: return minv, psfinv else: return minv