from devito import *
from examples.seismic.source import WaveletSource, RickerSource, GaborSource, TimeAxis
from examples.seismic import plot_image
import numpy as np
from sympy import init_printing, latex
='mathjax') init_printing(use_latex
06 bis - Elastic wave equation implementation on a staggered grid
This is a first attempt at implemenenting the elastic wave equation as described in:
[1] Jean Virieux (1986). ”P-SV wave propagation in heterogeneous media: Velocity‐stress finite‐difference method.” GEOPHYSICS, 51(4), 889-901. https://doi.org/10.1190/1.1442147
The current version actually attempts to mirror the FDELMODC implementation by Jan Thorbecke:
[2] https://janth.home.xs4all.nl/Software/fdelmodcManual.pdf
Explosive source
We will first attempt to replicate the explosive source test case described in [1], Figure 4. We start by defining the source signature \(g(t)\), the derivative of a Gaussian pulse, given by Eq 4:
\[g(t) = -2 \alpha(t - t_0)e^{-\alpha(t-t_0)^2}\]
# Initial grid: 1km x 1km, with spacing 100m
= (1500., 1500.)
extent = (201, 201)
shape = SpaceDimension(name='x', spacing=Constant(name='h_x', value=extent[0]/(shape[0]-1)))
x = SpaceDimension(name='z', spacing=Constant(name='h_z', value=extent[1]/(shape[1]-1)))
z = Grid(extent=extent, shape=shape, dimensions=(x, z)) grid
class DGaussSource(WaveletSource):
def wavelet(self, f0, t):
= 0.004
a return -2.*a*(t - 1/f0) * np.exp(-a * (t - 1/f0)**2)
# Timestep size from Eq. 7 with V_p=6000. and dx=100
= 0., 300.
t0, tn = (10. / np.sqrt(2.)) / 6.
dt = TimeAxis(start=t0, stop=tn, step=dt)
time_range
= RickerSource(name='src', grid=grid, f0=0.01, time_range=time_range)
src = [750., 750.] src.coordinates.data[:]
#NBVAL_SKIP
src.show()
# Now we create the velocity and pressure fields
= 2
so
= VectorTimeFunction(name='v', grid=grid, space_order=so, time_order=1)
v = TensorTimeFunction(name='t', grid=grid, space_order=so, time_order=1) tau
# Now let's try and create the staggered updates
= grid.stepping_dim
t = grid.time_dim
time
# We need some initial conditions
= 2.0
V_p = 1.0
V_s = 1.8
density
# The source injection term
= src.inject(field=tau.forward[0,0], expr=src)
src_xx = src.inject(field=tau.forward[1,1], expr=src)
src_zz
# Thorbecke's parameter notation
= V_p*V_p
cp2 = V_s*V_s
cs2 = 1/density
ro
= cs2*density
mu = (cp2*density - 2*mu)
l
# First order elastic wave equation
= v.dt - ro * div(tau)
pde_v = tau.dt - l * diag(div(v.forward)) - mu * (grad(v.forward) + grad(v.forward).transpose(inner=False))
pde_tau # Time update
= Eq(v.forward, solve(pde_v, v.forward))
u_v = Eq(tau.forward, solve(pde_tau, tau.forward))
u_t
= Operator([u_v] + [u_t] + src_xx + src_zz) op
u_v
\(\displaystyle \left[\begin{matrix}\operatorname{v_{x}}{\left(t + dt,x + \frac{h_{x}}{2},z \right)}\\\operatorname{v_{z}}{\left(t + dt,x,z + \frac{h_{z}}{2} \right)}\end{matrix}\right] = \left[\begin{matrix}dt \left(0.555555555555556 \frac{\partial}{\partial x} \operatorname{t_{xx}}{\left(t,x,z \right)} + 0.555555555555556 \frac{\partial}{\partial z} \operatorname{t_{xz}}{\left(t,x + \frac{h_{x}}{2},z + \frac{h_{z}}{2} \right)} + \frac{\operatorname{v_{x}}{\left(t,x + \frac{h_{x}}{2},z \right)}}{dt}\right)\\dt \left(0.555555555555556 \frac{\partial}{\partial x} \operatorname{t_{xz}}{\left(t,x + \frac{h_{x}}{2},z + \frac{h_{z}}{2} \right)} + 0.555555555555556 \frac{\partial}{\partial z} \operatorname{t_{zz}}{\left(t,x,z \right)} + \frac{\operatorname{v_{z}}{\left(t,x,z + \frac{h_{z}}{2} \right)}}{dt}\right)\end{matrix}\right]\)
#NBVAL_IGNORE_OUTPUT
=dt) op(dt
Operator `Kernel` ran in 0.19 s
PerformanceSummary([(PerfKey(name='section0', rank=None),
PerfEntry(time=0.175716, gflopss=0.0, gpointss=0.0, oi=0.0, ops=0, itershapes=[])),
(PerfKey(name='section1', rank=None),
PerfEntry(time=0.009343000000000011, gflopss=0.0, gpointss=0.0, oi=0.0, ops=0, itershapes=[]))])
#NBVAL_SKIP
# Let's see what we got....
0].data[0], vmin=-.5*1e-1, vmax=.5*1e-1, cmap="seismic")
plot_image(v[1].data[0], vmin=-.5*1e-2, vmax=.5*1e-2, cmap="seismic")
plot_image(v[0, 0].data[0], vmin=-.5*1e-2, vmax=.5*1e-2, cmap="seismic")
plot_image(tau[1,1].data[0], vmin=-.5*1e-2, vmax=.5*1e-2, cmap="seismic")
plot_image(tau[0,1].data[0], vmin=-.5*1e-2, vmax=.5*1e-2, cmap="seismic") plot_image(tau[
#NBVAL_IGNORE_OUTPUT
assert np.isclose(norm(v[0]), 0.6285093, atol=1e-4, rtol=0)
# Now that looks pretty! But let's do it again with a higher order...
= 12
so = VectorTimeFunction(name='v', grid=grid, space_order=so, time_order=1)
v = TensorTimeFunction(name='t', grid=grid, space_order=so, time_order=1)
tau # The source injection term
= src.inject(field=tau.forward[0,0], expr=src)
src_xx = src.inject(field=tau.forward[1,1], expr=src)
src_zz
# First order elastic wave equation
= v.dt - ro * div(tau)
pde_v = tau.dt - l * diag(div(v.forward)) - mu * (grad(v.forward) + grad(v.forward).transpose(inner=False))
pde_tau # Time update
= Eq(v.forward, solve(pde_v, v.forward))
u_v = Eq(tau.forward, solve(pde_tau, tau.forward))
u_t
= Operator([u_v]+ [u_t] + src_xx + src_zz ) op
#NBVAL_IGNORE_OUTPUT
0].data.fill(0.)
v[1].data.fill(0.)
v[0,0].data.fill(0.)
tau[0,1].data.fill(0.)
tau[1,1].data.fill(0.)
tau[
=dt) op(dt
Operator `Kernel` ran in 0.13 s
PerformanceSummary([(PerfKey(name='section0', rank=None),
PerfEntry(time=0.12092999999999998, gflopss=0.0, gpointss=0.0, oi=0.0, ops=0, itershapes=[])),
(PerfKey(name='section1', rank=None),
PerfEntry(time=0.005271000000000005, gflopss=0.0, gpointss=0.0, oi=0.0, ops=0, itershapes=[]))])
#NBVAL_SKIP
# Let's see what we got....
0].data[0], vmin=-.5*1e-2, vmax=.5*1e-2, cmap="seismic")
plot_image(v[1].data[0], vmin=-.5*1e-2, vmax=.5*1e-2, cmap="seismic")
plot_image(v[0, 0].data[0], vmin=-.5*1e-2, vmax=.5*1e-2, cmap="seismic")
plot_image(tau[1,1].data[0], vmin=-.5*1e-2, vmax=.5*1e-2, cmap="seismic")
plot_image(tau[0,1].data[0], vmin=-.5*1e-2, vmax=.5*1e-2, cmap="seismic") plot_image(tau[
#NBVAL_IGNORE_OUTPUT
assert np.isclose(norm(v[0]), 0.62521476, atol=1e-4, rtol=0)