|
| 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