Skip to content
This repository was archived by the owner on Feb 21, 2023. It is now read-only.

Commit 74bd4c8

Browse files
committed
Tests: added new to test to check tilted boundary gathers against conventional gathers
1 parent bc9c914 commit 74bd4c8

10 files changed

Lines changed: 4313082 additions & 0 deletions

tests/test_gathers.py

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import pytest
2+
3+
import numpy as np
4+
import pandas as pd
5+
6+
from examples.seismic import Model, TimeAxis, RickerSource, Receiver
7+
from devito import TimeFunction, Eq, solve, Operator
8+
from devitoboundary import ImmersedBoundary
9+
from devitoboundary.symbolics.symbols import x_b
10+
from devitoboundary.stencils.stencil_utils import generic_function
11+
12+
13+
def reference_shot(model, time_range, f0):
14+
"""
15+
Produce a reference shot gather with a level, conventional free-surface
16+
implementation.
17+
"""
18+
src = RickerSource(name='src', grid=model.grid, f0=f0,
19+
npoint=1, time_range=time_range)
20+
21+
# First, position source centrally in all dimensions, then set depth
22+
src.coordinates.data[0, :] = np.array(model.domain_size) * .5
23+
# Remember that 0, 0, 0 is top left corner
24+
# Depth is 100m from free-surface boundary
25+
src.coordinates.data[0, -1] = 600.
26+
27+
# Create symbol for 101 receivers
28+
rec = Receiver(name='rec', grid=model.grid, npoint=101,
29+
time_range=time_range)
30+
31+
# Prescribe even spacing for receivers along the x-axis
32+
rec.coordinates.data[:, 0] = np.linspace(0, model.domain_size[0], num=101)
33+
rec.coordinates.data[:, 1] = 500. # Centered on y axis
34+
rec.coordinates.data[:, 2] = 650. # Depth is 150m from free surface
35+
36+
# Define the wavefield with the size of the model and the time dimension
37+
u = TimeFunction(name="u", grid=model.grid, time_order=2, space_order=4)
38+
39+
# We can now write the PDE
40+
pde = model.m * u.dt2 - u.laplace + model.damp * u.dt
41+
42+
stencil = Eq(u.forward, solve(pde, u.forward))
43+
44+
# Finally we define the source injection and receiver read function
45+
src_term = src.inject(field=u.forward,
46+
expr=src * model.critical_dt**2 / model.m)
47+
48+
# Create interpolation expression for receivers
49+
rec_term = rec.interpolate(expr=u.forward)
50+
51+
x, y, z = model.grid.dimensions
52+
time = u.grid.stepping_dim
53+
# Throw a free surface in here
54+
free_surface_0 = Eq(u[time+1, x, y, 60], 0)
55+
free_surface_1 = Eq(u[time+1, x, y, 59], u[time+1, x, y, 61])
56+
free_surface_2 = Eq(u[time+1, x, y, 58], u[time+1, x, y, 62])
57+
free_surface = [free_surface_0, free_surface_1, free_surface_2]
58+
59+
op = Operator([stencil] + src_term + rec_term + free_surface)
60+
61+
op(time=time_range.num-1, dt=model.critical_dt)
62+
63+
return rec.data
64+
65+
66+
def tilted_shot(model, time_range, f0, tilt):
67+
"""
68+
Produce a shot for the same setup, but tilted with immersed free surface
69+
"""
70+
src = RickerSource(name='src', grid=model.grid, f0=f0,
71+
npoint=1, time_range=time_range)
72+
73+
# First, position source, then set depth
74+
src.coordinates.data[0, 0] = 500. - 100.*np.sin(np.radians(tilt))
75+
src.coordinates.data[0, 1] = 500.
76+
# Remember that 0, 0, 0 is top left corner
77+
# Depth is 100m from free-surface boundary
78+
src.coordinates.data[0, 2] = 500. + 100.*np.cos(np.radians(tilt))
79+
80+
# Create symbol for 101 receivers
81+
rec = Receiver(name='rec', grid=model.grid, npoint=101,
82+
time_range=time_range)
83+
84+
# Prescribe even spacing for receivers along the x-axis
85+
rec_center_x = 500. - 150.*np.sin(np.radians(tilt))
86+
rec_center_z = 500. + 150.*np.cos(np.radians(tilt))
87+
88+
rec_top_x = rec_center_x - 500.*np.cos(np.radians(tilt))
89+
rec_bottom_x = rec_center_x + 500.*np.cos(np.radians(tilt))
90+
91+
rec_top_z = rec_center_z - 500.*np.sin(np.radians(tilt))
92+
rec_bottom_z = rec_center_z + 500.*np.sin(np.radians(tilt))
93+
94+
rec.coordinates.data[:, 0] = np.linspace(rec_top_x, rec_bottom_x, num=101)
95+
rec.coordinates.data[:, 1] = 500. # Centered on y axis
96+
rec.coordinates.data[:, 2] = np.linspace(rec_top_z, rec_bottom_z, num=101)
97+
98+
# Define the wavefield with the size of the model and the time dimension
99+
u = TimeFunction(name="u", grid=model.grid, time_order=2, space_order=4,
100+
coefficients='symbolic')
101+
102+
infile = 'tests/trial_surfaces/angled_surface_'+str(tilt)+'.ply'
103+
104+
# Zero even derivatives on the boundary
105+
bcs_u = [Eq(generic_function(x_b, 2*i), 0)
106+
for i in range(1+u.space_order//2)]
107+
functions = pd.DataFrame({'function': [u],
108+
'bcs': [bcs_u]},
109+
columns=['function', 'bcs'])
110+
111+
# Create the immersed boundary surface
112+
surface = ImmersedBoundary('topography', infile, functions)
113+
# Configure derivative needed
114+
derivs = pd.DataFrame({'function': [u],
115+
'derivative': [2]},
116+
columns=['function', 'derivative'])
117+
coeffs = surface.subs(derivs)
118+
119+
# We can now write the PDE
120+
pde = model.m * u.dt2 - u.laplace + model.damp * u.dt
121+
122+
stencil = Eq(u.forward, solve(pde, u.forward),
123+
coefficients=coeffs['substitution'].values[0])
124+
125+
# Finally we define the source injection and receiver read function
126+
src_term = src.inject(field=u.forward,
127+
expr=src * model.critical_dt**2 / model.m)
128+
129+
# Create interpolation expression for receivers
130+
rec_term = rec.interpolate(expr=u.forward)
131+
132+
op = Operator([stencil] + src_term + rec_term)
133+
op(time=time_range.num-1, dt=model.critical_dt)
134+
135+
return rec.data
136+
137+
138+
class TestGathers:
139+
"""
140+
A class for testing the accuracy of gathers resulting from a reflection off
141+
the immersed boundary.
142+
"""
143+
144+
@pytest.mark.parametrize('tilt', [5, 10, 15, 20, 25, 30, 35, 40, 45])
145+
def test_tilted_boundary(self, tilt):
146+
"""
147+
Check that gathers for a tilted boundary match those generated with a
148+
conventional horizontal free surface and the same geometry.
149+
"""
150+
max_thres = 0.09
151+
avg_thres = 0.006
152+
# Define a physical size
153+
shape = (101, 101, 101) # Number of grid point (nx, ny, nz)
154+
spacing = (10., 10., 10.) # Grid spacing in m. The domain size is 1x1x1km
155+
origin = (0., 0., 0.)
156+
157+
v = 1.5
158+
159+
model = Model(vp=v, origin=origin, shape=shape, spacing=spacing,
160+
space_order=4, nbl=10, bcs="damp")
161+
162+
t0 = 0. # Simulation starts a t=0
163+
tn = 500. # Simulation last 0.5 seconds (500 ms)
164+
dt = model.critical_dt # Time step from model grid spacing
165+
166+
time_range = TimeAxis(start=t0, stop=tn, step=dt)
167+
168+
f0 = 0.010 # Source peak frequency is 10Hz (0.010 kHz)
169+
170+
ref = reference_shot(model, time_range, f0)
171+
tilted = tilted_shot(model, time_range, f0, tilt)
172+
173+
assert np.amax(np.absolute(ref - tilted)) < max_thres
174+
assert np.mean(np.absolute(ref-tilted)) < avg_thres

0 commit comments

Comments
 (0)