Skip to content

Commit 9475728

Browse files
authored
Merge pull request #14 from mrava87/master
MDD method
2 parents 0dcee2d + e15e2b7 commit 9475728

File tree

8 files changed

+338
-13
lines changed

8 files changed

+338
-13
lines changed

docs/source/api/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Wave-Equation processing
6161
:toctree: generated/
6262

6363
MDC
64+
MDD
6465
Marchenko
6566
Demigration
6667

pylops_distributed/LinearOperator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def _adjoint(self):
209209
self.todask[0]),
210210
dtype=self.dtype)
211211

212-
def div1(self, y, niter=100):
212+
def div(self, y, niter=100):
213213
r"""Solve the linear problem :math:`\mathbf{y}=\mathbf{A}\mathbf{x}`.
214214
215215
Overloading of operator ``/`` to improve expressivity of
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from .mdd import MDC
2-
#from .mdd import MDD
2+
from .mdd import MDD
33
from .marchenko import Marchenko
44
from .lsm import Demigration, LSM

pylops_distributed/waveeqprocessing/marchenko.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class Marchenko():
7979
8080
"""
8181
def __init__(self, R, nt, dt=0.004, dr=1., wav=None, toff=0.0,
82-
nsmooth=10, saveRt=True, prescaled=False, dtype='float32'):
82+
nsmooth=10, saveRt=False, prescaled=False, dtype='float32'):
8383
# Save inputs into class
8484
self.nt = nt
8585
self.dt = dt
@@ -105,7 +105,6 @@ def __init__(self, R, nt, dt=0.004, dr=1., wav=None, toff=0.0,
105105
# Save R
106106
self.Rtwosided_fft = R
107107

108-
109108
def apply_onepoint(self, trav, dist=None, G0=None, nfft=None,
110109
rtm=False, greens=False, dottest=False,
111110
**kwargs_cgls):

pylops_distributed/waveeqprocessing/mdd.py

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import logging
2+
import numpy as np
3+
import dask.array as da
24
from pylops.waveeqprocessing.mdd import _MDC
35

6+
from pylops_distributed.utils import dottest as Dottest
47
from pylops_distributed import Identity, Transpose
58
from pylops_distributed.signalprocessing import FFT, Fredholm1
9+
from pylops_distributed.optimization.cg import cgls
610

711
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.WARNING)
812

913

1014
def MDC(G, nt, nv, dt=1., dr=1., twosided=True,
11-
saveGt=True, conj=False, prescaled=False,
15+
saveGt=False, conj=False, prescaled=False,
1216
compute=(False, False), todask=(False, False)):
1317
r"""Multi-dimensional convolution.
1418
@@ -73,3 +77,126 @@ def MDC(G, nt, nv, dt=1., dr=1., twosided=True,
7377
(nt, G.shape[1], nv)),
7478
'todask': (todask[1], False),
7579
'compute':(False, compute[0])})
80+
81+
82+
83+
def MDD(G, d, dt=0.004, dr=1., nfmax=None, wav=None,
84+
twosided=True, adjoint=False, dottest=False,
85+
saveGt=False, add_negative=True, **kwargs_cgls):
86+
r"""Multi-dimensional deconvolution.
87+
88+
Solve multi-dimensional deconvolution problem using
89+
:py:func:`scipy.sparse.linalg.lsqr` iterative solver.
90+
91+
Parameters
92+
----------
93+
G : :obj:`dask.array.ndarray`
94+
Multi-dimensional convolution kernel in frequency domain of size
95+
:math:`[n_{f,max} \times n_s \times n_r]`
96+
d : :obj:`dask.array.ndarray`
97+
Data in time domain :math:`[n_t \times n_s (\times n_vs)]` if
98+
``twosided=False`` or ``twosided=True`` and ``add_negative=True``
99+
(with only positive times) or size
100+
:math:`[2*n_t-1 \times n_s (\times n_vs)]` if ``twosided=True``
101+
dt : :obj:`float`, optional
102+
Sampling of time integration axis
103+
dr : :obj:`float`, optional
104+
Sampling of receiver integration axis
105+
nfmax : :obj:`int`, optional
106+
Index of max frequency to include in deconvolution process
107+
wav : :obj:`numpy.ndarray`, optional
108+
Wavelet to convolve to the inverted model and psf
109+
(must be centered around its index in the middle of the array).
110+
If ``None``, the outputs of the inversion are returned directly.
111+
twosided : :obj:`bool`, optional
112+
MDC operator and data both negative and positive time (``True``)
113+
or only positive (``False``)
114+
add_negative : :obj:`bool`, optional
115+
Add negative side to MDC operator and data (``True``) or not
116+
(``False``)- operator and data are already provided with both positive
117+
and negative sides. To be used only with ``twosided=True``.
118+
adjoint : :obj:`bool`, optional
119+
Compute and return adjoint(s)
120+
dottest : :obj:`bool`, optional
121+
Apply dot-test
122+
saveGt : :obj:`bool`, optional
123+
Save ``G`` and ``G^H`` to speed up the computation of adjoint of
124+
:class:`pylops_distributed.signalprocessing.Fredholm1` (``True``) or
125+
create ``G^H`` on-the-fly (``False``) Note that ``saveGt=True`` will be
126+
faster but double the amount of required memory
127+
**kwargs_cgls
128+
Arbitrary keyword arguments for
129+
:py:func:`pylops_distributed.optimization.cg.cgls` solver
130+
131+
Returns
132+
-------
133+
minv : :obj:`dask.array.ndarray`
134+
Inverted model of size :math:`[n_t \times n_r (\times n_{vs})]`
135+
for ``twosided=False`` or
136+
:math:`[2*n_t-1 \times n_r (\times n_vs)]` for ``twosided=True``
137+
madj : :obj:`dask.array.ndarray`
138+
Adjoint model of size :math:`[n_t \times n_r (\times n_{vs})]`
139+
for ``twosided=False`` or
140+
:math:`[2*n_t-1 \times n_r (\times n_r) ]` for ``twosided=True``
141+
142+
See Also
143+
--------
144+
MDC : Multi-dimensional convolution
145+
146+
Notes
147+
-----
148+
Refer to :class:`pylops.waveeqprocessing.MDD` for implementation
149+
details. Note that this implementation is currently missing the ``wav``
150+
and ``causality_precond=False`` options.
151+
152+
"""
153+
nf, ns, nr = G.shape
154+
nt = d.shape[0]
155+
if len(d.shape) == 2:
156+
nv = 1
157+
else:
158+
nv = d.shape[2]
159+
if twosided:
160+
if add_negative:
161+
nt2 = 2 * nt - 1
162+
else:
163+
nt2 = nt
164+
nt = (nt2 + 1) // 2
165+
nfmax_allowed = int(np.ceil((nt2+1)/2))
166+
else:
167+
nt2 = nt
168+
nfmax_allowed = nt
169+
170+
# Fix nfmax to be at maximum equal to half of the size of fft samples
171+
if nfmax is None or nfmax > nfmax_allowed:
172+
nfmax = nfmax_allowed
173+
logging.warning('nfmax set equal to ceil[(nt+1)/2=%d]' % nfmax)
174+
175+
# Add negative part to data and model
176+
if twosided and add_negative:
177+
if nv == 1:
178+
d = da.concatenate((da.zeros((nt - 1, ns)), d), axis=0)
179+
else:
180+
d = da.concatenate((da.zeros((nt - 1, ns, nv)), d), axis=0)
181+
182+
# Define MDC linear operator
183+
MDCop = MDC(G, nt2, nv=nv, dt=dt, dr=dr,
184+
twosided=twosided, saveGt=saveGt)
185+
if dottest:
186+
Dottest(MDCop, nt2 * ns * nv, nt2 * nr * nv, verb=True)
187+
188+
# Adjoint
189+
if adjoint:
190+
madj = MDCop.H * d.flatten()
191+
madj = da.squeeze(madj.reshape(nt2, nr, nv))
192+
193+
# Inverse
194+
minv = cgls(MDCop, d.flatten(), **kwargs_cgls)[0]
195+
minv = da.squeeze(minv.reshape(nt2, nr, nv))
196+
#if wav is not None:
197+
# minv = sp_convolve1d(minv, wav, axis=-1)
198+
199+
if adjoint:
200+
return minv, madj
201+
else:
202+
return minv

pytests/test_waveeqprocessing.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from pylops.waveeqprocessing.mdd import MDC
99

1010
from pylops_distributed.waveeqprocessing.mdd import MDC as dMDC
11+
from pylops_distributed.waveeqprocessing.mdd import MDD
1112
from pylops_distributed.utils import dottest
1213

1314
PAR = {'ox': 0, 'dx': 2, 'nx': 10,
@@ -144,20 +145,32 @@ def test_MDC_1virtualsource(par):
144145
# Define MDC linear operator
145146
Gwav_fft = np.fft.fft(Gwav, par['nt2'], axis=-1)
146147
Gwav_fft = Gwav_fft[..., :par['nfmax']]
148+
dGwav_fft = da.from_array(Gwav_fft.transpose(2, 0, 1))
147149

148-
dMDCop = dMDC(da.from_array(Gwav_fft.transpose(2, 0, 1)),
149-
nt=par['nt2'], nv=1, dt=par['dt'], dr=par['dx'],
150+
dMDCop = dMDC(dGwav_fft, nt=par['nt2'], nv=1, dt=par['dt'], dr=par['dx'],
150151
twosided=par['twosided'])
151152
MDCop = MDC(Gwav_fft.transpose(2, 0, 1), nt=par['nt2'], nv=1,
152153
dt=par['dt'], dr=par['dx'], twosided=par['twosided'],
153154
transpose=False, dtype='float32')
154155
dottest(dMDCop, par['nt2'] * par['ny'], par['nt2'] * par['nx'],
155156
chunks=((par['nt2'] * par['ny'], par['nt2'] * par['nx'])))
156157

158+
# Compare results with pylops implementation
157159
mwav = mwav.T
158-
dy = (dMDCop * da.from_array(mwav.flatten())).compute()
160+
dy = dMDCop * da.from_array(mwav.flatten())
159161
y = MDCop * mwav.flatten()
160-
assert_array_almost_equal(dy, y, decimal=5)
162+
assert_array_almost_equal(dy.compute(), y, decimal=5)
163+
164+
# Apply mdd function
165+
dy = dy.reshape(par['nt2'], par['ny'])
166+
print(dy)
167+
minv = MDD(dGwav_fft,
168+
dy[par['nt'] - 1:] if par['twosided'] else dy,
169+
dt=par['dt'], dr=par['dx'], nfmax=par['nfmax'],
170+
twosided=par['twosided'], adjoint=False, dottest=False,
171+
**dict(niter=50))
172+
print('minv', minv)
173+
assert_array_almost_equal(mwav, minv.compute(), decimal=2)
161174

162175

163176
@pytest.mark.parametrize("par", [(par1), (par2), (par3), (par4),
@@ -204,8 +217,9 @@ def test_MDC_Nvirtualsources(par):
204217
# Define MDC linear operator
205218
Gwav_fft = np.fft.fft(Gwav, par['nt2'], axis=-1)
206219
Gwav_fft = Gwav_fft[..., :par['nfmax']]
220+
dGwav_fft = da.from_array(Gwav_fft.transpose(2, 0, 1))
207221

208-
dMDCop = dMDC(da.from_array(Gwav_fft.transpose(2, 0, 1)), nt=par['nt2'],
222+
dMDCop = dMDC(dGwav_fft, nt=par['nt2'],
209223
nv=par['nx'], dt=par['dt'], dr=par['dx'],
210224
twosided=par['twosided'])
211225
MDCop = MDC(Gwav_fft.transpose(2, 0, 1), nt=par['nt2'], nv=par['nx'],
@@ -217,7 +231,20 @@ def test_MDC_Nvirtualsources(par):
217231
chunks=((par['nt2'] * par['ny'] * par['nx'],
218232
par['nt2'] * par['nx'] * par['nx'])))
219233

234+
# Compare results with pylops implementation
220235
mwav = mwav.T
221-
dy = (dMDCop * da.from_array(mwav.flatten())).compute()
236+
dy = (dMDCop * da.from_array(mwav.flatten()))
222237
y = MDCop * mwav.flatten()
223-
assert_array_almost_equal(dy, y, decimal=5)
238+
assert_array_almost_equal(dy.compute(), y, decimal=5)
239+
240+
# Apply mdd function
241+
dy = dy.reshape(par['nt2'], par['ny'], par['nx'])
242+
243+
print(dy.shape)
244+
minv = MDD(dGwav_fft,
245+
dy[par['nt'] - 1:] if par['twosided'] else dy,
246+
dt=par['dt'], dr=par['dx'], nfmax=par['nfmax'],
247+
twosided=par['twosided'], adjoint=False,
248+
dottest=False, **dict(niter=50))
249+
print(mwav.shape, minv)
250+
assert_array_almost_equal(mwav, minv.compute(), decimal=2)

tutorials/marchenko.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,14 @@
1010
This allows lazy loading using a `Dask <https://dask.org>`_
1111
array and distributing over frequencies the computation of the various
1212
Fredholm integrals involved in the forward model.
13+
14+
**NOTE:** do not expect this code to run any fast than its `pylops equivalent
15+
<https://pylops.readthedocs.io/en/latest/tutorials/marchenko.html#sphx-glr-tutorials-marchenko-py>`_.
16+
for small datasets. The pylops-distributed framework should only be used when
17+
dealing with large datasets that do not fit in memory and benefit from
18+
distributed computing.
19+
1320
"""
14-
# sphinx_gallery_thumbnail_number = 3
1521
import warnings
1622
import numpy as np
1723
import dask.array as da
@@ -23,6 +29,8 @@
2329
warnings.filterwarnings('ignore')
2430
plt.close('all')
2531

32+
# sphinx_gallery_thumbnail_number = 4
33+
2634
###############################################################################
2735
# Let's start by defining some input parameters and loading the test data
2836

0 commit comments

Comments
 (0)