Skip to content

Commit 3b1c014

Browse files
committed
examples: Update superstepping script to use the examples.model
1 parent 41402ff commit 3b1c014

1 file changed

Lines changed: 105 additions & 130 deletions

File tree

Lines changed: 105 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,22 @@
11
""" Script that demonstrates the functionality of the superstep in 2D
22
Acoustic wave equation with source injection
33
"""
4-
import shutil
5-
import urllib.request
4+
import os
65
from argparse import ArgumentParser
7-
from collections.abc import Callable
8-
from dataclasses import dataclass
9-
from pathlib import Path
106

117
import matplotlib.pyplot as plt
128
import numpy as np
13-
from scipy.interpolate import interpn
149

1510
from devito import (
1611
ConditionalDimension,
1712
Eq,
18-
Function,
19-
Grid,
2013
Operator,
2114
SparseTimeFunction,
2215
TimeFunction,
2316
solve,
2417
)
2518
from devito.timestepping.superstep import superstep_generator
26-
27-
28-
@dataclass
29-
class model:
30-
name: str
31-
velocity: Callable
32-
# Spatial Domain
33-
shape: tuple[int]
34-
origin: tuple[float]
35-
extent: tuple[float]
36-
# Souce Location
37-
source: tuple[float]
38-
# Time Domain
39-
t0: float
40-
t1: float
41-
t2: float
42-
critical_dt: float
43-
# Plotting
44-
zlim: float
45-
46-
47-
def layered_velocity(grid, step=0):
48-
velocity = Function(name="layered", grid=grid, space_order=(2, step, step))
49-
q = grid.shape[1]//4
50-
velocity.data[:] = 1500
51-
velocity.data[:, q:2*q] = 2000
52-
velocity.data[:, 2*q:3*q] = 2500
53-
velocity.data[:, 3*q:] = 3000
54-
return velocity
55-
56-
57-
def marmousi(grid, step=0):
58-
# Grab dataset from pwd or download
59-
url = 'https://github.com/devitocodes/data/raw/refs/heads/master/Simple2D/vp_marmousi_bi' # noqa: E501
60-
filename = Path('marmousi.np')
61-
shape = (1601, 401)
62-
if not filename.exists():
63-
with urllib.request.urlopen(url) as response, open(filename, 'wb') as fh:
64-
shutil.copyfileobj(response, fh)
65-
data = np.fromfile(filename, dtype=np.float32, sep='').reshape(*shape)
66-
cropped = data[650:1051, 35:]
67-
68-
xs = np.linspace(0, 1, cropped.shape[0])
69-
ys = np.linspace(0, 1, cropped.shape[1])
70-
71-
xd = np.linspace(0, 1, grid.shape[0])
72-
yd = np.linspace(0, 1, grid.shape[1])
73-
74-
velocity = Function(name="marmousi", grid=grid, space_order=(2, step, step))
75-
velocity.data[:] = 1000*interpn(
76-
(xs, ys), cropped, np.meshgrid(xd, yd), method='nearest'
77-
).T
78-
return velocity
19+
from examples.seismic import demo_model, SeismicModel
7920

8021

8122
def ricker(t, f=10, A=1):
@@ -88,31 +29,35 @@ def ricker(t, f=10, A=1):
8829
return A * (1 - 2 * trm) * np.exp(-trm)
8930

9031

91-
def acoustic_model(model, step=1, snapshots=1):
32+
def acoustic_model(model, t0, t1, t2, critical_dt, source, step=1, snapshots=1):
9233
# Construct 2D Grid
93-
grid = Grid(shape=model.shape, extent=model.extent)
94-
x, y = grid.dimensions
95-
96-
t0, t1, t2 = model.t0, model.t1, model.t2
97-
98-
velocity = model.velocity(grid, step)
99-
u = TimeFunction(name="u", grid=grid, time_order=2, space_order=2)
34+
x, y = model.grid.dimensions
35+
velocity = model.vp
36+
u = TimeFunction(name="u", grid=model.grid, time_order=2, space_order=2)
10037

10138
pde = (1/velocity**2)*u.dt2 - u.laplace
10239
stencil = Eq(u.forward, solve(pde, u.forward))
10340

104-
nt1 = int(np.ceil((t1 - t0)/model.critical_dt))
41+
nt1 = int(np.ceil((t1 - t0)/critical_dt))
10542
dt = (t1 - t0)/nt1
10643

10744
# Source
10845
t = np.linspace(t0, t1, nt1)
10946
rick = ricker(t)
11047
source = SparseTimeFunction(
111-
name="ricker", npoint=1, coordinates=[model.source], nt=nt1, grid=grid,
112-
time_order=2, space_order=4
48+
name="ricker",
49+
npoint=1,
50+
coordinates=[source],
51+
nt=nt1,
52+
grid=model.grid,
53+
time_order=2,
54+
space_order=4
11355
)
11456
source.data[:, 0] = rick
115-
src_term = source.inject(field=u.forward, expr=source*velocity*velocity*dt*dt)
57+
src_term = source.inject(
58+
field=u.forward,
59+
expr=source*velocity**2*dt**2
60+
)
11661

11762
op1 = Operator([stencil] + src_term)
11863
op1(time=nt1 - 1, dt=dt)
@@ -121,7 +66,12 @@ def acoustic_model(model, step=1, snapshots=1):
12166
idx = nt1 % 3
12267
if step == 1:
12368
# Non-superstep case
124-
new_u = TimeFunction(name="new_u", grid=grid, time_order=2, space_order=2)
69+
new_u = TimeFunction(
70+
name="new_u",
71+
grid=model.grid,
72+
time_order=2,
73+
space_order=2
74+
)
12575
stencil = [stencil.subs(
12676
{u.forward: new_u.forward, u: new_u, u.backward: new_u.backward}
12777
)]
@@ -131,16 +81,23 @@ def acoustic_model(model, step=1, snapshots=1):
13181
else:
13282
new_u, new_u_p, *stencil = superstep_generator(u, stencil.rhs, step, nt=nt1)
13383

134-
nt2 = int(np.ceil((t2 - t1)/model.critical_dt))
84+
nt2 = int(np.ceil((t2 - t1)/critical_dt))
13585
dt = (t2 - t1)/nt2
13686

13787
# Snapshot the solution
13888
factor = int(np.ceil(nt2/(snapshots + 1)))
139-
t_sub = ConditionalDimension('t_sub', parent=grid.time_dim, factor=factor)
89+
t_sub = ConditionalDimension(
90+
't_sub',
91+
parent=model.grid.time_dim,
92+
factor=factor
93+
)
14094
u_save = TimeFunction(
141-
name='usave', grid=grid,
142-
time_order=0, space_order=2,
143-
save=snapshots//step + 1, time_dim=t_sub
95+
name='usave',
96+
grid=model.grid,
97+
time_order=0,
98+
space_order=2,
99+
save=snapshots//step + 1,
100+
time_dim=t_sub
144101
)
145102
save = Eq(u_save, new_u)
146103

@@ -153,72 +110,82 @@ def acoustic_model(model, step=1, snapshots=1):
153110
return u_save.data
154111

155112

156-
layered_model = model(
157-
name='layered',
158-
velocity=layered_velocity,
159-
shape=(101, 101),
160-
origin=(0., 0.),
161-
extent=(1000, 1000), # 1kmx1km
162-
source=(500, 20),
163-
t0=0,
164-
t1=0.2,
165-
t2=0.65,
166-
critical_dt=0.002357,
167-
zlim=30
168-
)
169-
170-
marmousi_model = model(
171-
name='marmousi',
172-
velocity=marmousi,
173-
shape=(274, 301),
174-
origin=(4875., 262.5),
175-
extent=(3000, 2737.5), # 3kmx2.7km
176-
source=(1000, 1200),
177-
t0=0,
178-
t1=0.2,
179-
t2=0.5,
180-
critical_dt=0.0013728,
181-
zlim=20
182-
)
183-
184-
185113
if __name__ == '__main__':
186114
parser = ArgumentParser()
187115
parser.add_argument('--model', default='layered', choices=['layered', 'marmousi'])
188116
args = parser.parse_args()
189117

190-
model = layered_model if args.model == 'layered' else marmousi_model
118+
t0 = 0
119+
t1 = 0.2
120+
if args.model == 'layered':
121+
source = (500, 20)
122+
t2 = 0.65
123+
critical_dt=0.002357
124+
zlim = 30
125+
else: # Marmousi
126+
# This requires the `devitocodes/data` repository, which we
127+
# assume to be checked out at `$VIRTUAL_ENV/src/data`.
128+
source = (1500, 1500)
129+
t2 = 0.5
130+
critical_dt=0.0013728
131+
zlim = 20
132+
tmp_model = demo_model(
133+
'marmousi-isotropic',
134+
space_order=2,
135+
data_path=f'{os.environ["VIRTUAL_ENV"]}/src/data',
136+
nbl=0
137+
)
138+
cropped = tmp_model.vp.data[400:701, -321:-20]
191139

192140
# Supersteps
193141
k = [1, 4]
194142
# Snapshots
195143
m = 13
196144
fig, axes = plt.subplots(len(k), m)
197145

198-
# Velocity model
199-
grid = Grid(shape=model.shape, extent=model.extent)
200-
v = model.velocity(grid)
201-
plot_extent = [
202-
model.origin[0],
203-
model.origin[0] + model.extent[0],
204-
model.origin[1] + model.extent[1],
205-
model.origin[1]
206-
]
207-
208146
for step, ax_row in zip(k, axes, strict=True):
209-
data = acoustic_model(model, step=step, snapshots=m)
210-
time = np.linspace(model.t1, model.t2, (m - 1)//step + 1)
147+
# Redefine the model every iteration because we need to adjust
148+
# the space order
149+
if args.model == 'layered':
150+
model = demo_model(
151+
'layers-isotropic',
152+
space_order=(2, step, step),
153+
nlayers=4,
154+
vp_top=1500,
155+
vp_bottom=3000,
156+
nbl=0
157+
)
158+
else: # Marmousi
159+
model = SeismicModel(
160+
space_order=(2, step, step),
161+
vp=1000*cropped,
162+
nbl=0,
163+
origin=(0, 0),
164+
shape=cropped.shape,
165+
spacing=(10, 10)
166+
)
167+
168+
plot_extent = [
169+
model.origin[0],
170+
model.origin[0] + model.grid.extent[0],
171+
model.origin[1] + model.grid.extent[1],
172+
model.origin[1]
173+
]
174+
data = acoustic_model(
175+
model, t0, t1, t2, critical_dt, source, step=step, snapshots=m
176+
)
177+
time = np.linspace(t1, t2, (m - 1)//step + 1)
211178
idx = 0
212179
for ii, ax in enumerate(ax_row):
213180
if ii % step == 0:
214181
ax.imshow(
215182
data[idx, :, :].T,
216183
extent=plot_extent,
217-
vmin=-model.zlim, vmax=model.zlim,
184+
vmin=-zlim, vmax=zlim,
218185
cmap='seismic'
219186
)
220-
ax.imshow(v.data.T, cmap='grey', extent=plot_extent, alpha=0.2)
221-
ax.set_title(f't = {time[idx]:0.3f}')
187+
ax.imshow(model.vp.data.T, cmap='grey', extent=plot_extent, alpha=0.2)
188+
ax.set_title(f't={time[idx]:0.3f}')
222189
idx += 1
223190
if ii > 0:
224191
ax.set_xticklabels([])
@@ -227,13 +194,21 @@ def acoustic_model(model, step=1, snapshots=1):
227194
xticks = ax.get_xticks()
228195
ax.set_xticks(np.array((
229196
model.origin[0],
230-
round(model.origin[0] + model.extent[0]/2, -3),
231-
model.origin[0] + model.extent[0]
197+
model.origin[0] + model.grid.extent[0]
232198
)))
233-
ax.set_xlim(model.origin[0], model.origin[0] + model.extent[0])
199+
ax.set_xlim(
200+
model.origin[0],
201+
model.origin[0] + model.grid.extent[0]
202+
)
234203
yticks = ax.get_yticks()
235-
ax.set_yticks(np.concat(((model.origin[1],), yticks[2::2])))
236-
ax.set_ylim(model.origin[1] + model.extent[1], model.origin[1])
204+
ax.set_yticks(np.array((
205+
model.origin[1],
206+
model.origin[1] + model.grid.extent[1]
207+
)))
208+
ax.set_ylim(
209+
model.origin[1] + model.grid.extent[1],
210+
model.origin[1]
211+
)
237212
else:
238213
ax.remove()
239214

@@ -246,4 +221,4 @@ def acoustic_model(model, step=1, snapshots=1):
246221
wspace=0.06,
247222
hspace=0.06
248223
)
249-
fig.savefig(f'{model.name}.png', dpi=300)
224+
fig.savefig(f'{args.model}.png', dpi=300)

0 commit comments

Comments
 (0)