|
| 1 | +""" Script that demonstrates the functionality of the superstep in 2D |
| 2 | +Acoustic wave equation with source injection |
| 3 | +""" |
| 4 | +import os |
| 5 | +from argparse import ArgumentParser |
| 6 | + |
| 7 | +import matplotlib.pyplot as plt |
| 8 | +import numpy as np |
| 9 | + |
| 10 | +from devito import ( |
| 11 | + ConditionalDimension, |
| 12 | + Eq, |
| 13 | + Operator, |
| 14 | + SparseTimeFunction, |
| 15 | + TimeFunction, |
| 16 | + solve, |
| 17 | +) |
| 18 | +from devito.timestepping.superstep import superstep_generator |
| 19 | +from examples.seismic import demo_model, SeismicModel |
| 20 | + |
| 21 | + |
| 22 | +def ricker(t, f=10, A=1): |
| 23 | + """ |
| 24 | + The Ricker wavelet |
| 25 | + f - freq in Hz |
| 26 | + A - amplitude |
| 27 | + """ |
| 28 | + trm = (np.pi * f * (t - 1 / f)) ** 2 |
| 29 | + return A * (1 - 2 * trm) * np.exp(-trm) |
| 30 | + |
| 31 | + |
| 32 | +def acoustic_model(model, t0, t1, t2, critical_dt, source, step=1, snapshots=1): |
| 33 | + # Construct 2D Grid |
| 34 | + x, y = model.grid.dimensions |
| 35 | + velocity = model.vp |
| 36 | + u = TimeFunction(name="u", grid=model.grid, time_order=2, space_order=2) |
| 37 | + |
| 38 | + pde = (1/velocity**2)*u.dt2 - u.laplace |
| 39 | + stencil = Eq(u.forward, solve(pde, u.forward)) |
| 40 | + |
| 41 | + nt1 = int(np.ceil((t1 - t0)/critical_dt)) |
| 42 | + dt = (t1 - t0)/nt1 |
| 43 | + |
| 44 | + # Source |
| 45 | + t = np.linspace(t0, t1, nt1) |
| 46 | + rick = ricker(t) |
| 47 | + source = SparseTimeFunction( |
| 48 | + name="ricker", |
| 49 | + npoint=1, |
| 50 | + coordinates=[source], |
| 51 | + nt=nt1, |
| 52 | + grid=model.grid, |
| 53 | + time_order=2, |
| 54 | + space_order=4 |
| 55 | + ) |
| 56 | + source.data[:, 0] = rick |
| 57 | + src_term = source.inject( |
| 58 | + field=u.forward, |
| 59 | + expr=source*velocity**2*dt**2 |
| 60 | + ) |
| 61 | + |
| 62 | + op1 = Operator([stencil] + src_term) |
| 63 | + op1(time=nt1 - 1, dt=dt) |
| 64 | + |
| 65 | + # Stencil and operator |
| 66 | + idx = nt1 % 3 |
| 67 | + if step == 1: |
| 68 | + # Non-superstep case |
| 69 | + # In this case we need to create a new `TimeFunction` and copy |
| 70 | + # the previous soluton into that new function. This is necessary |
| 71 | + # when a rotating buffer is used in the `TimeFunction` and the |
| 72 | + # order of the timesteps is not necessarily the right order for |
| 73 | + # resuming the simulation. We also create a new stencil that |
| 74 | + # writes to the new `TimeFunction`. |
| 75 | + new_u = TimeFunction( |
| 76 | + name="new_u", |
| 77 | + grid=model.grid, |
| 78 | + time_order=2, |
| 79 | + space_order=2 |
| 80 | + ) |
| 81 | + stencil = [stencil.subs( |
| 82 | + {u.forward: new_u.forward, u: new_u, u.backward: new_u.backward} |
| 83 | + )] |
| 84 | + new_u.data[0, :] = u.data[idx - 2] |
| 85 | + new_u.data[1, :] = u.data[idx - 1] |
| 86 | + new_u.data[2, :] = u.data[idx] |
| 87 | + else: |
| 88 | + new_u, new_u_p, *stencil = superstep_generator(u, stencil.rhs, step, nt=nt1) |
| 89 | + |
| 90 | + nt2 = int(np.ceil((t2 - t1)/critical_dt)) |
| 91 | + dt = (t2 - t1)/nt2 |
| 92 | + |
| 93 | + # Snapshot the solution |
| 94 | + factor = int(np.ceil(nt2/(snapshots + 1))) |
| 95 | + t_sub = ConditionalDimension( |
| 96 | + 't_sub', |
| 97 | + parent=model.grid.time_dim, |
| 98 | + factor=factor |
| 99 | + ) |
| 100 | + u_save = TimeFunction( |
| 101 | + name='usave', |
| 102 | + grid=model.grid, |
| 103 | + time_order=0, |
| 104 | + space_order=2, |
| 105 | + save=snapshots//step + 1, |
| 106 | + time_dim=t_sub |
| 107 | + ) |
| 108 | + save = Eq(u_save, new_u) |
| 109 | + |
| 110 | + op = Operator([*stencil, save]) |
| 111 | + op(dt=dt) |
| 112 | + |
| 113 | + if step == 1: |
| 114 | + u_save.data[0, :, :] = u.data[idx] |
| 115 | + |
| 116 | + return u_save.data |
| 117 | + |
| 118 | + |
| 119 | +if __name__ == '__main__': |
| 120 | + parser = ArgumentParser() |
| 121 | + parser.add_argument('--model', default='layered', choices=['layered', 'marmousi']) |
| 122 | + args = parser.parse_args() |
| 123 | + |
| 124 | + t0 = 0 |
| 125 | + t1 = 0.2 |
| 126 | + if args.model == 'layered': |
| 127 | + source = (500, 20) |
| 128 | + t2 = 0.65 |
| 129 | + critical_dt = 0.002357 |
| 130 | + zlim = 30 |
| 131 | + else: # Marmousi |
| 132 | + # This requires the `devitocodes/data` repository, which we |
| 133 | + # assume to be checked out at `$VIRTUAL_ENV/src/data`. |
| 134 | + source = (1500, 1500) |
| 135 | + t2 = 0.5 |
| 136 | + critical_dt = 0.0013728 |
| 137 | + zlim = 20 |
| 138 | + try: |
| 139 | + path = f'{os.environ["VIRTUAL_ENV"]}/src' |
| 140 | + except KeyError: |
| 141 | + path = str(os.environ['GITHUB_WORKSPACE']) |
| 142 | + tmp_model = demo_model( |
| 143 | + 'marmousi-isotropic', |
| 144 | + space_order=2, |
| 145 | + data_path=f'{path}/data', |
| 146 | + nbl=0 |
| 147 | + ) |
| 148 | + cropped = tmp_model.vp.data[400:701, -321:-20] |
| 149 | + |
| 150 | + # Supersteps |
| 151 | + k = [1, 4] |
| 152 | + # Snapshots |
| 153 | + m = 13 |
| 154 | + fig, axes = plt.subplots(len(k), m) |
| 155 | + |
| 156 | + for step, ax_row in zip(k, axes, strict=True): |
| 157 | + # Redefine the model every iteration because we need to adjust |
| 158 | + # the space order |
| 159 | + if args.model == 'layered': |
| 160 | + model = demo_model( |
| 161 | + 'layers-isotropic', |
| 162 | + space_order=(2, step, step), |
| 163 | + nlayers=4, |
| 164 | + vp_top=1500, |
| 165 | + vp_bottom=3000, |
| 166 | + nbl=0 |
| 167 | + ) |
| 168 | + else: # Marmousi |
| 169 | + model = SeismicModel( |
| 170 | + space_order=(2, step, step), |
| 171 | + vp=1000*cropped, |
| 172 | + nbl=0, |
| 173 | + origin=(0, 0), |
| 174 | + shape=cropped.shape, |
| 175 | + spacing=(10, 10) |
| 176 | + ) |
| 177 | + |
| 178 | + plot_extent = [ |
| 179 | + model.origin[0], |
| 180 | + model.origin[0] + model.grid.extent[0], |
| 181 | + model.origin[1] + model.grid.extent[1], |
| 182 | + model.origin[1] |
| 183 | + ] |
| 184 | + data = acoustic_model( |
| 185 | + model, t0, t1, t2, critical_dt, source, step=step, snapshots=m |
| 186 | + ) |
| 187 | + time = np.linspace(t1, t2, (m - 1)//step + 1) |
| 188 | + idx = 0 |
| 189 | + for ii, ax in enumerate(ax_row): |
| 190 | + if ii % step == 0: |
| 191 | + ax.imshow( |
| 192 | + data[idx, :, :].T, |
| 193 | + extent=plot_extent, |
| 194 | + vmin=-zlim, vmax=zlim, |
| 195 | + cmap='seismic' |
| 196 | + ) |
| 197 | + ax.imshow(model.vp.data.T, cmap='grey', extent=plot_extent, alpha=0.2) |
| 198 | + ax.set_title(f't={time[idx]:0.3f}') |
| 199 | + idx += 1 |
| 200 | + if ii > 0: |
| 201 | + ax.set_xticklabels([]) |
| 202 | + ax.set_yticklabels([]) |
| 203 | + else: |
| 204 | + xticks = ax.get_xticks() |
| 205 | + ax.set_xticks(np.array(( |
| 206 | + model.origin[0], |
| 207 | + model.origin[0] + model.grid.extent[0] |
| 208 | + ))) |
| 209 | + ax.set_xlim( |
| 210 | + model.origin[0], |
| 211 | + model.origin[0] + model.grid.extent[0] |
| 212 | + ) |
| 213 | + yticks = ax.get_yticks() |
| 214 | + ax.set_yticks(np.array(( |
| 215 | + model.origin[1], |
| 216 | + model.origin[1] + model.grid.extent[1] |
| 217 | + ))) |
| 218 | + ax.set_ylim( |
| 219 | + model.origin[1] + model.grid.extent[1], |
| 220 | + model.origin[1] |
| 221 | + ) |
| 222 | + else: |
| 223 | + ax.remove() |
| 224 | + |
| 225 | + fig.set_size_inches(16, 3.5) |
| 226 | + fig.subplots_adjust( |
| 227 | + left=0.05, |
| 228 | + bottom=0.025, |
| 229 | + right=0.99, |
| 230 | + top=0.97, |
| 231 | + wspace=0.06, |
| 232 | + hspace=0.06 |
| 233 | + ) |
| 234 | + fig.savefig(f'{args.model}.png', dpi=300) |
0 commit comments