11""" Script that demonstrates the functionality of the superstep in 2D
22Acoustic wave equation with source injection
33"""
4- import shutil
5- import urllib .request
4+ import os
65from argparse import ArgumentParser
7- from collections .abc import Callable
8- from dataclasses import dataclass
9- from pathlib import Path
106
117import matplotlib .pyplot as plt
128import numpy as np
13- from scipy .interpolate import interpn
149
1510from devito import (
1611 ConditionalDimension ,
1712 Eq ,
18- Function ,
19- Grid ,
2013 Operator ,
2114 SparseTimeFunction ,
2215 TimeFunction ,
2316 solve ,
2417)
2518from 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
8122def 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-
185113if __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