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 _PoststackLinearModelling(wav, nt0, spatdims=None, explicit=False, sparse=False, _MatrixMult=MatrixMult, _Convolve1D=Convolve1D, _FirstDerivative=FirstDerivative, args_MatrixMult={}, args_Convolve1D={}, args_FirstDerivative={}): """Post-stack linearized seismic modelling operator. Used to be able to provide operators from different libraries to PoststackLinearModelling. It operates in the same way as public method (PoststackLinearModelling) but has additional input parameters allowing passing a different operator and additional arguments to be passed to such operator. """ if len(wav.shape) == 2 and wav.shape[0] != nt0: raise ValueError('Provide 1d wavelet or 2d wavelet composed of nt0 ' 'wavelets') # organize dimensions if spatdims is None: dims = (nt0, ) spatdims = None elif isinstance(spatdims, int): dims = (nt0, spatdims) spatdims = (spatdims, ) else: dims = (nt0, ) + 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 # Create wavelet operator if len(wav.shape) == 1: C = convmtx(wav, nt0)[:, len(wav) // 2:-len(wav) // 2 + 1] else: C = nonstationary_convmtx(wav, nt0, hc=wav.shape[1] // 2, pad=(nt0, nt0)) # Combine operators M = np.dot(C, D) if sparse: M = csc_matrix(M) Pop = _MatrixMult(M, dims=spatdims, **args_MatrixMult) else: # Create wavelet operator if len(wav.shape) == 1: Cop = _Convolve1D(np.prod(np.array(dims)), h=wav, offset=len(wav) // 2, dir=0, dims=dims, **args_Convolve1D) else: Cop = _MatrixMult(nonstationary_convmtx(wav, nt0, hc=wav.shape[1] // 2, pad=(nt0, nt0)), dims=spatdims, **args_MatrixMult) # Create derivative operator Dop = _FirstDerivative(np.prod(np.array(dims)), dims=dims, dir=0, sampling=1., **args_FirstDerivative) Pop = Cop * Dop return Pop
def PrestackWaveletModelling(m, theta, nwav, wavc=None, vsvp=0.5, linearization='akirich'): r"""Pre-stack linearized seismic modelling operator for wavelet. Create operator to be applied to a wavelet for generation of band-limited seismic angle gathers using a linearized version of the Zoeppritz equation. Parameters ---------- m : :obj:`np.ndarray` elastic parameter profles of size :math:`[n_{t0} \times N]` where :math:`N=3/2`. Note that the ``dtype`` of this variable will define that of the operator theta : :obj:`int` Incident angles in degrees. Must have same ``dtype`` of ``m`` (or it will be automatically casted to it) nwav : :obj:`np.ndarray` Number of samples of wavelet to be applied/estimated wavc : :obj:`int`, optional Index of the center of the wavelet vsvp : :obj:`np.ndarray` or :obj:`float`, optional VS/VP ratio linearization : :obj:`str`, optional choice of linearization, ``akirich``: Aki-Richards, ``fatti``: Fatti, ``ps``: PS, or any function on the form of ``pylops.avo.avo.akirichards`` Returns ------- Mconv : :obj:`LinearOperator` pre-stack modelling operator for wavelet estimation. Raises ------ NotImplementedError If ``linearization`` is not an implemented linearization Notes ----- Pre-stack seismic modelling for wavelet estimate is the process of constructing seismic reflectivities using 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`: .. math:: d(t, \theta) = \sum_{i=1}^N G_i(t, \theta) m_i(t) * w(t) where :math:`w(t)` is the time domain seismic wavelet. In compact form: .. math:: \mathbf{d}= \mathbf{G} \mathbf{w} On the other hand, pre-stack wavelet estimation aims at recovering the wavelet given knowledge of the band-limited seismic pre-stack data and the elastic parameter profiles. """ ncp = get_array_module(theta) # define dtype to be used dtype = m.dtype # ensure m.dtype rules that of operator theta = theta.astype(dtype) # Create vsvp profile vsvp = vsvp if isinstance(vsvp, ncp.ndarray) else \ vsvp * ncp.ones(m.shape[0], dtype=dtype) wavc = nwav // 2 if wavc is None else wavc nt0 = len(vsvp) ntheta = len(theta) # 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 D = ncp.diag(0.5 * np.ones(nt0 - 1, dtype=dtype), k=1) - \ ncp.diag(0.5 * np.ones(nt0 - 1, dtype=dtype), k=-1) D[0] = D[-1] = 0 D = get_block_diag(theta)(*([D] * nG)) # Create infinite-reflectivity data M = ncp.dot(G, ncp.dot(D, m.T.flatten())).reshape(ntheta, nt0) Mconv = VStack([ MatrixMult(convmtx(M[itheta], nwav)[wavc:-nwav + wavc + 1], dtype=dtype) for itheta in range(ntheta) ]) return Mconv
def PoststackLinearModelling(wav, nt0, ndims=None, explicit=False): r"""Post-stack linearized seismic modelling operator. Create operator to be applied to acoustic impedance profile for generation of band-limited seismic post-stack data. Parameters ---------- wav : :obj:`np.ndarray` Wavelet in time domain (must had odd number of elements and centered to zero) nt0 : :obj:`int` Number of samples along time axis ndims : :obj:`int` or :obj:`tuple` Number of samples along horizontal axws explicit : :obj:`bool`, optional Create a chained linear operator (``False``, preffered for large data) or a ``MatrixMult`` linear operator with dense matrix (``True``, preffered for small data) Returns ------- Postop : :obj:`LinearOperator` post-stack modelling operator. Notes ----- Post-stack seismic modelling is the process of constructing seismic post-stack data from a profile of acoustic impedance in time (or depth) domain. This can be easily achieved using the following forward model: .. math:: d(t, \theta=0) = w(t) * \frac{dln(AI(t))}{dt} where :math:`AI(t)` is the acoustic impedance profile and :math:`w(t)` is the time domain seismic wavelet. In compact form: .. math:: \mathbf{d}= \mathbf{W} \mathbf{AI} On the other hand, post-stack inversion aims at recovering the impedance profile from the band-limited seismic stack data. """ if ndims is None: nspat = (1, ) ndims = (nt0, ) elif isinstance(ndims, int): nspat = (ndims, ) ndims = (nt0, ndims) else: nspat = ndims ndims = (nt0, ) + ndims 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 # Create wavelet operator C = convmtx(wav, nt0)[:, len(wav) // 2:-len(wav) // 2 + 1] # Combine operators M = np.dot(C, D) return MatrixMult(M, dims=nspat) else: # Create wavelet operator Cop = Convolve1D(np.prod(np.array(ndims)), h=wav, offset=len(wav) // 2, dir=0, dims=ndims) # Create derivative operator Dop = FirstDerivative(np.prod(np.array(ndims)), dims=ndims, dir=0, sampling=1.) return Cop * Dop
def _PoststackLinearModelling(wav, nt0, spatdims=None, explicit=False, sparse=False, kind='centered', _MatrixMult=MatrixMult, _Convolve1D=Convolve1D, _FirstDerivative=FirstDerivative, args_MatrixMult={}, args_Convolve1D={}, args_FirstDerivative={}): """Post-stack linearized seismic modelling operator. Used to be able to provide operators from different libraries to PoststackLinearModelling. It operates in the same way as public method (PoststackLinearModelling) but has additional input parameters allowing passing a different operator and additional arguments to be passed to such operator. """ 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 = wav.dtype # ensure wav.dtype rules that of operator if len(wav.shape) == 2 and wav.shape[0] != nt0: raise ValueError('Provide 1d wavelet or 2d wavelet composed of nt0 ' 'wavelets') # organize dimensions if spatdims is None: dims = (nt0, ) spatdims = None elif isinstance(spatdims, int): dims = (nt0, spatdims) spatdims = (spatdims, ) else: dims = (nt0, ) + spatdims if explicit: # 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), -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 # Create wavelet operator if len(wav.shape) == 1: C = convmtx(wav, nt0)[:, len(wav) // 2:-len(wav) // 2 + 1] else: C = nonstationary_convmtx(wav, nt0, hc=wav.shape[1] // 2, pad=(nt0, nt0)) # Combine operators M = ncp.dot(C, D) if sparse: M = get_csc_matrix(wav)(M) Pop = _MatrixMult(M, dims=spatdims, dtype=dtype, **args_MatrixMult) else: # Create wavelet operator if len(wav.shape) == 1: Cop = _Convolve1D(np.prod(np.array(dims)), h=wav, offset=len(wav) // 2, dir=0, dims=dims, dtype=dtype, **args_Convolve1D) else: Cop = _MatrixMult(nonstationary_convmtx(wav, nt0, hc=wav.shape[1] // 2, pad=(nt0, nt0)), dims=spatdims, dtype=dtype, **args_MatrixMult) # Create derivative operator Dop = _FirstDerivative(np.prod(np.array(dims)), dims=dims, dir=0, sampling=1., kind=kind, dtype=dtype, **args_FirstDerivative) Pop = Cop * Dop return Pop
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
def PrestackWaveletModelling(m, theta, nwav, wavc=None, vsvp=0.5, linearization='akirich'): r"""Pre-stack linearized seismic modelling operator for wavelet. Create operator to be applied to a wavelet for generation of band-limited seismic angle gathers using a linearized version of the Zoeppritz equation. Parameters ---------- m : :obj:`np.ndarray` elastic parameter profles of size :math:`[n_{t0} \times N]` where :math:`N=3/2` theta : :obj:`int` Incident angles in degrees nwav : :obj:`np.ndarray` Number of samples of wavelet to be applied/estimated wavc : :obj:`int`, optional Index of the center of the wavelet vsvp : :obj:`np.ndarray` or :obj:`float`, optional VS/VP ratio linearization : :obj:`str`, optional choice of linearization, ``akirich``: Aki-Richards, ``fatti``: Fatti Returns ------- Mconv : :obj:`LinearOperator` pre-stack modelling operator for wavelet estimation. Raises ------ NotImplementedError If ``linearization`` is not an implemented linearization Notes ----- Pre-stack seismic modelling for wavelet estimate is the process of constructing seismic reflectivities using 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`: .. math:: d(t, \theta) = \sum_{i=1}^N G_i(t, \theta) m_i(t) * w(t) where :math:`w(t)` is the time domain seismic wavelet. In compact form: .. math:: \mathbf{d}= \mathbf{G} \mathbf{w} On the other hand, pre-stack wavelet estimation aims at recovering the wavelet given knowledge of the band-limited seismic pre-stack data and the elastic parameter profiles. """ # Create vsvp profile vsvp = vsvp if isinstance(vsvp, np.ndarray) else vsvp * np.ones(m.shape[0]) wavc = nwav // 2 if wavc is None else wavc nt0 = len(vsvp) ntheta = len(theta) # 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 infinite-reflectivity data M = np.dot(G, np.dot(D, m.T.flatten())).reshape(ntheta, nt0) Mconv = VStack([ MatrixMult(convmtx(M[itheta], nwav)[wavc:-nwav + wavc + 1]) for itheta in range(ntheta) ]) return Mconv
def PoststackLinearModelling(wav, nt0, spatdims=None, explicit=False, sparse=False): r"""Post-stack linearized seismic modelling operator. Create operator to be applied to an acoustic impedance trace (or stack of traces) for generation of band-limited seismic post-stack data. The input model and data have shape :math:`[n_{t0} (\times n_x \times n_y)]`. Parameters ---------- wav : :obj:`np.ndarray` Wavelet in time domain (must had odd number of elements and centered to zero) nt0 : :obj:`int` Number of samples along time axis spatdims : :obj:`int` or :obj:`tuple`, optional Number of samples along spatial axis (or axes) (``None`` if only one dimension is available) 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) sparse : :obj:`bool`, optional Create a sparse matrix (``True``) or dense (``False``) when ``explicit=True`` Returns ------- Pop : :obj:`LinearOperator` post-stack modelling operator. Notes ----- Post-stack seismic modelling is the process of constructing seismic post-stack data from a profile of acoustic impedance in time (or depth) domain. This can be easily achieved using the following forward model: .. math:: d(t, \theta=0) = w(t) * \frac{dln(AI(t))}{dt} where :math:`AI(t)` is the acoustic impedance profile and :math:`w(t)` is the time domain seismic wavelet. In compact form: .. math:: \mathbf{d}= \mathbf{W} \mathbf{D} \mathbf{ai} On the other hand, post-stack inversion aims at recovering the impedance profile from the band-limited seismic stack data. """ # organize dimensions if spatdims is None: dims = (nt0, ) spatdims = None elif isinstance(spatdims, int): dims = (nt0, spatdims) spatdims = (spatdims,) else: dims = (nt0, ) + 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 # Create wavelet operator C = convmtx(wav, nt0)[:, len(wav) // 2:-len(wav) // 2 + 1] # Combine operators M = np.dot(C, D) if sparse: M = csc_matrix(M) Pop = 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 derivative operator Dop = FirstDerivative(np.prod(np.array(dims)), dims=dims, dir=0, sampling=1.) Pop = Cop*Dop return Pop