Skip to content

Commit cd783f1

Browse files
committed
doc: added mdd tutorial
1 parent 6d3b1e8 commit cd783f1

File tree

1 file changed

+234
-0
lines changed

1 file changed

+234
-0
lines changed

tutorials/mdd.py

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
"""
2+
Multi-Dimensional Deconvolution
3+
===============================
4+
This example shows how to set-up and run a Multi-Dimensional Deconvolution
5+
problem in a distributed fashion, leveraging the :py:class:`pylops_mpi.waveeqprocessing.MDC`
6+
class.
7+
8+
More precisely, compared to its counterpart in the PyLops documentation, this example distributes
9+
the frequency slices of the kernel of the MDC operator across multiple processes. Whilst both the
10+
entire model and data sit on all processes, within the MDC operator, and more precisely when the
11+
:py:class:`pylops_mpi.signalprocessing.Fredholm1` is called, different groups of frequencies are
12+
processed by the different ranks.
13+
14+
"""
15+
16+
import numpy as np
17+
from scipy.signal import filtfilt
18+
from matplotlib import pyplot as plt
19+
from mpi4py import MPI
20+
21+
from pylops.utils.seismicevents import hyperbolic2d, makeaxis
22+
from pylops.utils.tapers import taper3d
23+
from pylops.utils.wavelets import ricker
24+
25+
import pylops_mpi
26+
from pylops_mpi.DistributedArray import local_split, Partition
27+
28+
plt.close("all")
29+
rank = MPI.COMM_WORLD.Get_rank()
30+
size = MPI.COMM_WORLD.Get_size()
31+
dtype = np.float32
32+
cdtype = np.complex64
33+
34+
###############################################################################
35+
# Let's start by creating a set of hyperbolic events to be used as
36+
# our MDC kernel as well as the model
37+
38+
# Input parameters
39+
par = {
40+
"ox": -300,
41+
"dx": 10,
42+
"nx": 61,
43+
"oy": -500,
44+
"dy": 10,
45+
"ny": 101,
46+
"ot": 0,
47+
"dt": 0.004,
48+
"nt": 400,
49+
"f0": 20,
50+
"nfmax": 200,
51+
}
52+
53+
t0_m = 0.2
54+
vrms_m = 1100.0
55+
amp_m = 1.0
56+
57+
t0_G = (0.2, 0.5, 0.7)
58+
vrms_G = (1200.0, 1500.0, 2000.0)
59+
amp_G = (1.0, 0.6, 0.5)
60+
61+
# Taper
62+
tap = taper3d(par["nt"], (par["ny"], par["nx"]), (5, 5), tapertype="hanning")
63+
64+
# Create axis
65+
t, t2, x, y = makeaxis(par)
66+
67+
# Create wavelet
68+
wav = ricker(t[:41], f0=par["f0"])[0]
69+
70+
# Generate model
71+
mrefl, mwav = hyperbolic2d(x, t, t0_m, vrms_m, amp_m, wav)
72+
73+
# Generate operator
74+
G, Gwav = np.zeros((par["ny"], par["nx"], par["nt"])), np.zeros(
75+
(par["ny"], par["nx"], par["nt"])
76+
)
77+
for iy, y0 in enumerate(y):
78+
G[iy], Gwav[iy] = hyperbolic2d(x - y0, t, t0_G, vrms_G, amp_G, wav)
79+
G, Gwav = G * tap, Gwav * tap
80+
81+
# Add negative part to data and model
82+
mrefl = np.concatenate((np.zeros((par["nx"], par["nt"] - 1)), mrefl), axis=-1)
83+
mwav = np.concatenate((np.zeros((par["nx"], par["nt"] - 1)), mwav), axis=-1)
84+
Gwav2 = np.concatenate((np.zeros((par["ny"], par["nx"], par["nt"] - 1)), Gwav), axis=-1)
85+
86+
# Move to frequency
87+
Gwav_fft = np.fft.rfft(Gwav2, 2 * par["nt"] - 1, axis=-1)
88+
Gwav_fft = (Gwav_fft[..., : par["nfmax"]])
89+
90+
# Move frequency/time to first axis
91+
mrefl, mwav = mrefl.T, mwav.T
92+
Gwav_fft = Gwav_fft.transpose(2, 0, 1)
93+
94+
# Choose how to split frequencies to ranks
95+
nf = par["nfmax"]
96+
nf_rank = local_split((nf,), MPI.COMM_WORLD, Partition.SCATTER, 0)
97+
nf_ranks = np.concatenate(MPI.COMM_WORLD.allgather(nf_rank))
98+
ifin_rank = np.insert(np.cumsum(nf_ranks)[:-1], 0, 0)[rank]
99+
ifend_rank = np.cumsum(nf_ranks)[rank]
100+
101+
# Extract batch of frequency slices (in practice, this will be directly read from input file)
102+
G = Gwav_fft[ifin_rank:ifend_rank].astype(cdtype)
103+
104+
###############################################################################
105+
# Let's now define the distributed operator and model as well as compute the
106+
# data
107+
108+
# Define operator
109+
MDCop = pylops_mpi.waveeqprocessing.MPIMDC((1.0 * par["dt"] * np.sqrt(par["nt"])) * G,
110+
nt=2 * par["nt"] - 1, nv=1, nfreq=nf,
111+
dt=par["dt"], dr=1.0, twosided=True,
112+
fftengine="scipy", prescaled=True)
113+
114+
# Create model
115+
m = pylops_mpi.DistributedArray(global_shape=(2 * par["nt"] - 1) * par["nx"] * 1,
116+
partition=Partition.BROADCAST,
117+
dtype=dtype)
118+
m[:] = mrefl.astype(dtype).ravel()
119+
120+
# Create data
121+
d = MDCop @ m
122+
dloc = d.asarray().real.reshape(2 * par["nt"] - 1, par["ny"])
123+
124+
###############################################################################
125+
# Let's display what we have so far: operator, input model, and data
126+
127+
if rank == 0:
128+
fig, axs = plt.subplots(1, 2, figsize=(8, 6))
129+
axs[0].imshow(
130+
Gwav2[int(par["ny"] / 2)].T,
131+
aspect="auto",
132+
interpolation="nearest",
133+
cmap="gray",
134+
vmin=-np.abs(Gwav2.max()),
135+
vmax=np.abs(Gwav2.max()),
136+
extent=(x.min(), x.max(), t2.max(), t2.min()),
137+
)
138+
axs[0].set_title("G - inline view", fontsize=15)
139+
axs[0].set_xlabel(r"$x_R$")
140+
axs[1].set_ylabel(r"$t$")
141+
axs[1].imshow(
142+
Gwav2[:, int(par["nx"] / 2)].T,
143+
aspect="auto",
144+
interpolation="nearest",
145+
cmap="gray",
146+
vmin=-np.abs(Gwav2.max()),
147+
vmax=np.abs(Gwav2.max()),
148+
extent=(y.min(), y.max(), t2.max(), t2.min()),
149+
)
150+
axs[1].set_title("G - inline view", fontsize=15)
151+
axs[1].set_xlabel(r"$x_S$")
152+
axs[1].set_ylabel(r"$t$")
153+
fig.tight_layout()
154+
155+
fig, axs = plt.subplots(1, 2, figsize=(8, 6))
156+
axs[0].imshow(
157+
mwav,
158+
aspect="auto",
159+
interpolation="nearest",
160+
cmap="gray",
161+
vmin=-np.abs(mwav.max()),
162+
vmax=np.abs(mwav.max()),
163+
extent=(x.min(), x.max(), t2.max(), t2.min()),
164+
)
165+
axs[0].set_title(r"$m$", fontsize=15)
166+
axs[0].set_xlabel(r"$x_R$")
167+
axs[0].set_ylabel(r"$t$")
168+
axs[1].imshow(
169+
dloc,
170+
aspect="auto",
171+
interpolation="nearest",
172+
cmap="gray",
173+
vmin=-np.abs(dloc.max()),
174+
vmax=np.abs(dloc.max()),
175+
extent=(x.min(), x.max(), t2.max(), t2.min()),
176+
)
177+
axs[1].set_title(r"$d$", fontsize=15)
178+
axs[1].set_xlabel(r"$x_S$")
179+
axs[1].set_ylabel(r"$t$")
180+
fig.tight_layout()
181+
182+
###############################################################################
183+
# We are now ready to compute the adjoint (i.e., cross-correlation) and invert
184+
# back for our input model
185+
186+
# Adjoint
187+
madj = MDCop.H @ d
188+
madjloc = madj.asarray().real.reshape(2 * par["nt"] - 1, par["nx"])
189+
190+
# Inverse
191+
m0 = pylops_mpi.DistributedArray(global_shape=(2 * par["nt"] - 1) * par["nx"] * 1,
192+
partition=Partition.BROADCAST,
193+
dtype=cdtype)
194+
m0[:] = 0
195+
minv = pylops_mpi.cgls(MDCop, d, x0=m0, niter=50, show=True if rank == 0 else False)[0]
196+
minvloc = minv.asarray().real.reshape(2 * par["nt"] - 1, par["nx"])
197+
198+
if rank == 0:
199+
fig = plt.figure(figsize=(8, 6))
200+
ax1 = plt.subplot2grid((1, 5), (0, 0), colspan=2)
201+
ax2 = plt.subplot2grid((1, 5), (0, 2), colspan=2)
202+
ax3 = plt.subplot2grid((1, 5), (0, 4))
203+
ax1.imshow(
204+
madjloc,
205+
aspect="auto",
206+
interpolation="nearest",
207+
cmap="gray",
208+
vmin=-np.abs(madjloc.max()),
209+
vmax=np.abs(madjloc.max()),
210+
extent=(x.min(), x.max(), t2.max(), t2.min()),
211+
)
212+
ax1.set_title("Adjoint m", fontsize=15)
213+
ax1.set_xlabel(r"$x_V$")
214+
ax1.set_ylabel(r"$t$")
215+
ax2.imshow(
216+
minvloc,
217+
aspect="auto",
218+
interpolation="nearest",
219+
cmap="gray",
220+
vmin=-np.abs(minvloc.max()),
221+
vmax=np.abs(minvloc.max()),
222+
extent=(x.min(), x.max(), t2.max(), t2.min()),
223+
)
224+
ax2.set_title("Inverted m", fontsize=15)
225+
ax2.set_xlabel(r"$x_V$")
226+
ax2.set_ylabel(r"$t$")
227+
ax3.plot(
228+
madjloc[:, int(par["nx"] / 2)] / np.abs(madjloc[:, int(par["nx"] / 2)]).max(), t2, "r", lw=5
229+
)
230+
ax3.plot(
231+
minvloc[:, int(par["nx"] / 2)] / np.abs(minvloc[:, int(par["nx"] / 2)]).max(), t2, "k", lw=3
232+
)
233+
ax3.set_ylim([t2[-1], t2[0]])
234+
fig.tight_layout()

0 commit comments

Comments
 (0)