import os
import matplotlib.pyplot as plt
import numpy as np
from scipy.signal import hilbert
from devito import TimeFunction, Eq, solve, Operator, ConditionalDimension
from devito.types.dimension import CustomDimension
from devitopro.types.enriched import NoLayers
from devitopro import *
from examples.seismic import demo_model, TimeAxis, RickerSourceCUDA Calls
As with C backends, custom callbacks can be introduced to CUDA kernels generated by Devito. This notebook serves as a supplement to ccalls.ipynb, demonstrating how the approach showcased therein can be extended to other languages, such as CUDA.
#NBVAL_IGNORE_OUTPUT
so = 8
nbl = 30
model = demo_model(preset='layers-isotropic', nlayers=5,
shape=(301, 301), spacing=(10., 10.),
space_order=so, nbl=nbl)t0, tn = -100., 1200.
dt = model.critical_dt # Time step from model grid spacing
time_range = TimeAxis(start=t0, stop=tn, step=dt)
f0 = 0.020 # Source peak frequency is 20Hz (0.020 kHz)
src = RickerSource(name='src', grid=model.grid, f0=f0,
npoint=1, time_range=time_range, dtype=np.complex64)
src.data[:] = hilbert(np.real(src.data), axis=0)
# First, position source centrally in all dimensions, then set depth
src.coordinates.data[0, :] = np.array(model.domain_size) * .5
src.coordinates.data[0, -1] = 20. # Depth is 20mu = TimeFunction(name='u', grid=model.grid, time_order=2, space_order=so, dtype=np.complex64)pde = model.m * u.dt2 - u.laplace + model.damp * u.dt
stencil = Eq(u.forward, solve(pde, u.forward))src_term = src.inject(field=u.forward, expr=src*dt**2/model.m)#NBVAL_SKIP
# Set to the location of your Cuda installation
cufftdir = "/opt/nvhpc/math_libs"
class CuFFTCall(CCall):
"""Abstract class for C calls using CuFFT"""
header = 'updown_cuda.h' # Header containing CuFFT calls
header_dirs = (os.path.abspath(""), f'{cufftdir}/include/')
libs = ('cufft',)
lib_dirs = (f'{cufftdir}/lib64',)
class FwdFFT(CuFFTCall):
"""Forward FFT using CuFFT"""
name = 'fftFwd'
class InvFFT(CuFFTCall):
"""Forward FFT using CuFFT"""
name = 'fftInv'
# Snapshotting is set up broadly as per usual
nsnaps = 30
factor = int(np.ceil(src.nt/nsnaps))
time_subsampled = ConditionalDimension('t_sub', parent=model.grid.time_dim, factor=factor)
# Upgoing and downgoing wavefield snapshots
# NoLayers disables streaming/compression for ease of plotting the wavefields in this notebook
usave_u = TimeFunction(name='usaveu', grid=model.grid, time_order=0, space_order=so,
save=nsnaps, time_dim=time_subsampled, dtype=np.complex64,
layers=NoLayers)
usave_d = TimeFunction(name='usaved', grid=model.grid, time_order=0, space_order=so,
save=nsnaps, time_dim=time_subsampled, dtype=np.complex64,
layers=NoLayers)
# Temporaries as destination for upward and downward portions of
# Fourier-transformed wavefield
tmp_u = TimeFunction(name='tmpu', grid=model.grid, space_order=so, time_order=0, dtype=np.complex64)
tmp_d = TimeFunction(name='tmpd', grid=model.grid, space_order=so, time_order=0, dtype=np.complex64)
# CustomDimensions used to apply up-down filters to the transformed wavefield
y = model.grid.dimensions[-1]
negk = CustomDimension(name='negk', symbolic_min=y.symbolic_min-so, symbolic_max=y.symbolic_max//2-1, parent=y)
posk = CustomDimension(name='posk', symbolic_min=y.symbolic_max//2+1, symbolic_max=y.symbolic_max+so, parent=y)
# Upward and downward filtering operations
upfilter = Eq(tmp_u.subs(y, posk), 0, implicit_dims=time_subsampled)
downfilter = Eq(tmp_d.subs(y, negk), 0, implicit_dims=time_subsampled)
save = [FwdFFT([u.dmap, tmp_u.dmap, tmp_d.dmap,
u.symbolic_shape[1], model.grid.shape[-1] + 2*so,
u.symbolic_shape[2], model.grid.time_dim],
implicit_dims=time_subsampled),
upfilter, downfilter,
InvFFT([tmp_u.dmap, tmp_d.dmap,
u.symbolic_shape[1], model.grid.shape[-1] + 2*so,
u.symbolic_shape[2]],
implicit_dims=time_subsampled),
Eq(usave_u, tmp_u), Eq(usave_d, tmp_d)]#NBVAL_SKIP
op_split = Operator([stencil] + save + src_term)#NBVAL_SKIP
op_split(dt=dt)#NBVAL_SKIP
def plot_field(field, model, nbl, title):
clip = 0.5 # Clip value
vmax = clip*np.amax(np.abs(field[-1]))
vmin = -vmax
plt.imshow(model.vp.data[nbl:-nbl, nbl:-nbl].T, cmap='Greys')
plt.imshow(field[-1, nbl:-nbl, nbl:-nbl].T, cmap='seismic',
vmin=vmin, vmax=vmax, alpha=0.7)
plt.title(title)
plt.show()
plot_field(np.real(usave_u.data) + np.real(usave_d.data), model, nbl, "Full")
plot_field(np.real(usave_u.data), model, nbl, "Upgoing")
plot_field(np.real(usave_d.data), model, nbl, "Downgoing")