def PrestackLinearModelling(wav, theta, vsvp=0.5, nt0=1, spatdims=None, linearization='akirich', explicit=False, kind='centered'): r"""Pre-stack linearized seismic modelling operator. Create operator to be applied to elastic property profiles for generation of band-limited seismic angle gathers from a linearized version of the Zoeppritz equation. The input model must be arranged in a vector of size :math:`n_m \times n_{t0} (\times n_x \times n_y)` for ``explicit=True`` and :math:`n_{t0} \times n_m (\times n_x \times n_y)` for ``explicit=False``. Similarly the output data is arranged in a vector of size :math:`n_{theta} \times n_{t0} (\times n_x \times n_y)` for ``explicit=True`` and :math:`n_{t0} \times n_{theta} (\times n_x \times n_y)` for ``explicit=False``. Parameters ---------- wav : :obj:`np.ndarray` Wavelet in time domain (must had odd number of elements and centered to zero). Note that the ``dtype`` of this variable will define that of the operator theta : :obj:`np.ndarray` Incident angles in degrees. Must have same ``dtype`` of ``wav`` (or it will be automatically casted to it) vsvp : :obj:`float` or :obj:`np.ndarray` VS/VP ratio (constant or time/depth variant) nt0 : :obj:`int`, optional number of samples (if ``vsvp`` is a scalar) spatdims : :obj:`int` or :obj:`tuple`, optional Number of samples along spatial axis (or axes) (``None`` if only one dimension is available) linearization : :obj:`str` or :obj:`func`, optional choice of linearization, ``akirich``: Aki-Richards, ``fatti``: Fatti, ``ps``: PS or any function on the form of ``pylops.avo.avo.akirichards`` explicit : :obj:`bool`, optional Create a chained linear operator (``False``, preferred for large data) or a ``MatrixMult`` linear operator with dense matrix (``True``, preferred for small data) kind : :obj:`str`, optional Derivative kind (``forward`` or ``centered``). Returns ------- Preop : :obj:`LinearOperator` pre-stack modelling operator. Raises ------ NotImplementedError If ``linearization`` is not an implemented linearization NotImplementedError If ``kind`` is not ``forward`` nor ``centered`` Notes ----- Pre-stack seismic modelling is the process of constructing seismic pre-stack data from three (or two) profiles of elastic parameters in time (or depth) domain. This can be easily achieved using the following forward model: .. math:: d(t, \theta) = w(t) * \sum_{i=1}^{n_m} G_i(t, \theta) m_i(t) where :math:`w(t)` is the time domain seismic wavelet. In compact form: .. math:: \mathbf{d}= \mathbf{G} \mathbf{m} On the other hand, pre-stack inversion aims at recovering the different profiles of elastic properties from the band-limited seismic pre-stack data. """ ncp = get_array_module(wav) # check kind is correctly selected if kind not in ['forward', 'centered']: raise NotImplementedError('%s not an available derivative kind...' % kind) # define dtype to be used dtype = theta.dtype # ensure theta.dtype rules that of operator theta = theta.astype(dtype) # create vsvp profile vsvp = vsvp if isinstance(vsvp, ncp.ndarray) else \ vsvp * ncp.ones(nt0, dtype=dtype) nt0 = len(vsvp) ntheta = len(theta) # organize dimensions if spatdims is None: dims = (nt0, ntheta) spatdims = None elif isinstance(spatdims, int): dims = (nt0, ntheta, spatdims) spatdims = (spatdims, ) else: dims = (nt0, ntheta) + spatdims if explicit: # Create AVO operator if linearization == 'akirich': G = akirichards(theta, vsvp, n=nt0) elif linearization == 'fatti': G = fatti(theta, vsvp, n=nt0) elif linearization == 'ps': G = ps(theta, vsvp, n=nt0) elif callable(linearization): G = linearization(theta, vsvp, n=nt0) else: logging.error('%s not an available linearization...', linearization) raise NotImplementedError('%s not an available linearization...' % linearization) nG = len(G) G = [ ncp.hstack([ ncp.diag(G_[itheta] * ncp.ones(nt0, dtype=dtype)) for G_ in G ]) for itheta in range(ntheta) ] G = ncp.vstack(G).reshape(ntheta * nt0, nG * nt0) # Create derivative operator if kind == 'centered': D = ncp.diag(0.5 * ncp.ones(nt0 - 1, dtype=dtype), k=1) - \ ncp.diag(0.5 * ncp.ones(nt0 - 1, dtype=dtype), k=-1) D[0] = D[-1] = 0 else: D = ncp.diag(ncp.ones(nt0 - 1, dtype=dtype), k=1) - \ ncp.diag(ncp.ones(nt0, dtype=dtype), k=0) D[-1] = 0 D = get_block_diag(theta)(*([D] * nG)) # Create wavelet operator C = convmtx(wav, nt0)[:, len(wav) // 2:-len(wav) // 2 + 1] C = [C] * ntheta C = get_block_diag(theta)(*C) # Combine operators M = ncp.dot(C, ncp.dot(G, D)) Preop = MatrixMult(M, dims=spatdims, dtype=dtype) else: # Create wavelet operator Cop = Convolve1D(np.prod(np.array(dims)), h=wav, offset=len(wav) // 2, dir=0, dims=dims, dtype=dtype) # create AVO operator AVOop = AVOLinearModelling(theta, vsvp, spatdims=spatdims, linearization=linearization, dtype=dtype) # Create derivative operator dimsm = list(dims) dimsm[1] = AVOop.npars Dop = FirstDerivative(np.prod(np.array(dimsm)), dims=dimsm, dir=0, sampling=1., kind=kind, dtype=dtype) Preop = Cop * AVOop * Dop return Preop
def PrestackLinearModelling(wav, theta, vsvp=0.5, nt0=1, spatdims=None, linearization='akirich', explicit=False): r"""Pre-stack linearized seismic modelling operator. Create operator to be applied to elastic property profiles for generation of band-limited seismic angle gathers from a linearized version of the Zoeppritz equation. Parameters ---------- wav : :obj:`np.ndarray` Wavelet in time domain (must had odd number of elements and centered to zero) theta : :obj:`np.ndarray` Incident angles in degrees vsvp : :obj:`float` or :obj:`np.ndarray` VS/VP ratio (constant or time/depth variant) nt0 : :obj:`int`, optional number of samples (if ``vsvp`` is a scalar) spatdims : :obj:`int` or :obj:`tuple`, optional Number of samples along spatial axis (or axes) (``None`` if only one dimension is available) linearization : :obj:`str`, optional choice of linearization, ``akirich``: Aki-Richards, ``fatti``: Fatti explicit : :obj:`bool`, optional Create a chained linear operator (``False``, preferred for large data) or a ``MatrixMult`` linear operator with dense matrix (``True``, preferred for small data) Returns ------- Preop : :obj:`LinearOperator` pre-stack modelling operator. Raises ------ NotImplementedError If ``linearization`` is not an implemented linearization Notes ----- Pre-stack seismic modelling is the process of constructing seismic pre-stack data from three (or two) profiles of elastic parameters in time (or depth) domain arranged in an input vector :math:`\mathbf{m}` of size :math:`nt0 \times N`. This can be easily achieved using the following forward model: .. math:: d(t, \theta) = w(t) * \sum_{i=1}^N G_i(t, \theta) m_i(t) where :math:`w(t)` is the time domain seismic wavelet. In compact form: .. math:: \mathbf{d}= \mathbf{G} \mathbf{m} On the other hand, pre-stack inversion aims at recovering the different profiles of elastic properties from the band-limited seismic pre-stack data. """ # create vsvp profile vsvp = vsvp if isinstance(vsvp, np.ndarray) else vsvp * np.ones(nt0) nt0 = len(vsvp) ntheta = len(theta) # organize dimensions if spatdims is None: dims = (nt0, ntheta) spatdims = None elif isinstance(spatdims, int): dims = (nt0, ntheta, spatdims) spatdims = (spatdims, ) else: dims = (nt0, ntheta) + spatdims if explicit: # Create derivative operator D = np.diag(0.5 * np.ones(nt0 - 1), k=1) - \ np.diag(0.5 * np.ones(nt0 - 1), -1) D[0] = D[-1] = 0 D = block_diag(*([D] * 3)) # Create AVO operator if linearization == 'akirich': G1, G2, G3 = akirichards(theta, vsvp, n=nt0) elif linearization == 'fatti': G1, G2, G3 = fatti(theta, vsvp, n=nt0) else: logging.error('%s not an available linearization...', linearization) raise NotImplementedError('%s not an available linearization...' % linearization) G = [ np.hstack((np.diag(G1[itheta] * np.ones(nt0)), np.diag(G2[itheta] * np.ones(nt0)), np.diag(G3[itheta] * np.ones(nt0)))) for itheta in range(ntheta) ] G = np.vstack(G).reshape(ntheta * nt0, 3 * nt0) # Create wavelet operator C = convmtx(wav, nt0)[:, len(wav) // 2:-len(wav) // 2 + 1] C = [C] * ntheta C = block_diag(*C) # Combine operators M = np.dot(C, np.dot(G, D)) return MatrixMult(M, dims=spatdims) else: # Create wavelet operator Cop = Convolve1D(np.prod(np.array(dims)), h=wav, offset=len(wav) // 2, dir=0, dims=dims) # create AVO operator AVOop = AVOLinearModelling(theta, vsvp, spatdims=spatdims, linearization=linearization) # Create derivative operator dimsm = list(dims) dimsm[1] = AVOop.npars Dop = FirstDerivative(np.prod(np.array(dimsm)), dims=dimsm, dir=0, sampling=1.) return Cop * AVOop * Dop