Skip to content

Commit 949ef5f

Browse files
committed
add directory comparing numerical and analytical solutions for constant density acoustic wave equation
1 parent 3fb8e4b commit 949ef5f

File tree

4 files changed

+263
-9
lines changed

4 files changed

+263
-9
lines changed

mms/acoustic_2D_mms.py

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
4+
from simwave import (
5+
SpaceModel, TimeModel, RickerWavelet, Wavelet, Solver, Compiler,
6+
Receiver, Source, plot_wavefield, plot_shotrecord, plot_velocity_model
7+
)
8+
9+
10+
def initial_shape(space_model, kx, kz):
11+
nbl_pad_width = space_model.nbl_pad_width
12+
halo_pad_width = space_model.halo_pad_width
13+
bbox = space_model.bounding_box
14+
# values
15+
xmin, xmax = bbox[0: 2]
16+
zmin, zmax = bbox[2: 4]
17+
x_coords = np.linspace(xmin, xmax, space_model.shape[0])
18+
z_coords = np.linspace(zmin, zmax, space_model.shape[1])
19+
X, Z = np.meshgrid(x_coords, z_coords)
20+
21+
return np.sin(kx * X) * np.sin(kz * Z)
22+
23+
24+
class MySource(Source):
25+
26+
@property
27+
def interpolated_points_and_values(self):
28+
""" Calculate the amplitude value at all points"""
29+
30+
points = np.array([], dtype=np.uint)
31+
values = np.array([], dtype=self.space_model.dtype)
32+
33+
nbl_pad_width = self.space_model.nbl_pad_width
34+
halo_pad_width = self.space_model.halo_pad_width
35+
dimension = self.space_model.dimension
36+
bbox = self.space_model.bounding_box
37+
38+
for dim, dim_length in enumerate(self.space_model.shape):
39+
40+
# points
41+
lpad = halo_pad_width[dim][0] + nbl_pad_width[dim][0]
42+
rpad = halo_pad_width[dim][1] + nbl_pad_width[dim][1] + dim_length
43+
points = np.append(points, np.array([lpad, rpad], dtype=np.uint))
44+
# values
45+
coor_min, coor_max = bbox[dim * dimension:2 + dim * dimension]
46+
coordinates = np.linspace(coor_min, coor_max, dim_length)
47+
amplitudes = np.sin(np.pi / (coor_max - coor_min) * coordinates)
48+
values = np.append(values, amplitudes.astype(values.dtype))
49+
# offset
50+
offset = np.array([0, values.size], dtype=np.uint)
51+
52+
return points, values, offset
53+
54+
def cosenoid(time_model, amplitude, omega=2*np.pi*1):
55+
"""Function that generates the signal satisfying s(0) = ds/dt(0) = 0"""
56+
return amplitude * np.cos(omega * time_model.time_values)
57+
58+
def create_models(Lx, Lz, vel, tf, h, p):
59+
# create the space model
60+
space_model = SpaceModel(
61+
bounding_box=(0, Lx, 0, Lz),
62+
grid_spacing=(h, h),
63+
velocity_model=vel,
64+
space_order=p,
65+
dtype=np.float32
66+
)
67+
68+
# config boundary conditions
69+
# (none, null_dirichlet or null_neumann)
70+
space_model.config_boundary(
71+
damping_length=0,
72+
boundary_condition=(
73+
"null_dirichlet", "null_dirichlet",
74+
"null_dirichlet", "null_dirichlet"
75+
),
76+
damping_polynomial_degree=3,
77+
damping_alpha=0.001
78+
)
79+
80+
# create the time model
81+
time_model = TimeModel(
82+
space_model=space_model,
83+
tf=tf,
84+
saving_stride=1
85+
)
86+
87+
return space_model, time_model
88+
89+
def geometry_acquisition(space_model, sources=None, receivers=None):
90+
if sources is None:
91+
sources=[(2560, 2560)]
92+
if receivers is None:
93+
receivers=[(2560, i) for i in range(0, 5120, 10)]
94+
95+
# create the set of sources
96+
# source = Source(
97+
# space_model,
98+
# coordinates=sources,
99+
# window_radius=4
100+
# )
101+
102+
# personalized source
103+
my_source = MySource(space_model, coordinates=[])
104+
105+
# crete the set of receivers
106+
receiver = Receiver(
107+
space_model=space_model,
108+
coordinates=receivers,
109+
window_radius=4
110+
)
111+
112+
return my_source, receiver
113+
114+
def build_solver(space_model, time_model, freq, vp, kx, kz, omega):
115+
116+
# geometry acquisition
117+
source, receiver = geometry_acquisition(space_model)
118+
119+
# create a ricker wavelet with 10hz of peak frequency
120+
# ricker = RickerWavelet(freq, time_model)
121+
122+
# create a cosenoid wavelet
123+
amplitude = (kx**2+kz**2) - omega**2/vp**2
124+
# amplitude = 0
125+
wavelet = Wavelet(cosenoid, time_model=time_model, amplitude=amplitude, omega=omega)
126+
127+
# set compiler options
128+
compiler = Compiler( cc='gcc', language='cpu_openmp', cflags='-O3 -fPIC -ffast-math -Wall -std=c99 -shared')
129+
130+
# create the solver
131+
solver = Solver(
132+
space_model=space_model,
133+
time_model=time_model,
134+
sources=source,
135+
receivers=receiver,
136+
# wavelet=ricker,
137+
wavelet=wavelet,
138+
compiler=compiler
139+
)
140+
141+
return solver
142+
143+
144+
if __name__ == "__main__":
145+
146+
# problem params
147+
Lx, Lz = 5120, 5120
148+
tf = 1.5
149+
freq = 5
150+
vp = 1500
151+
152+
# discretization params
153+
n = 513
154+
h = 10
155+
p = 4
156+
157+
# harmonic parameters
158+
kx = np.pi / Lx
159+
kz = np.pi / Lz
160+
omega = 2*np.pi*freq
161+
162+
# velocity model
163+
vel = vp * np.ones(shape=(n, n), dtype=np.float32)
164+
165+
# discretization
166+
space_model, time_model = create_models(Lx, Lz, vel, tf, h, p)
167+
168+
# initial grid
169+
# initial_grid = np.zeros(shape=space_model.shape)
170+
initial_grid = initial_shape(space_model, kx, kz)
171+
172+
# get solver
173+
solver = build_solver(space_model, time_model, freq, vp, kx, kz, omega)
174+
175+
# run the forward
176+
u_full, recv = solver.forward(
177+
[initial_grid * np.cos(-1 * time_model.dt * omega),
178+
initial_grid * np.cos(-0 * time_model.dt * omega)
179+
]
180+
)
181+
182+
# plot stuff
183+
print("u_full shape:", u_full.shape)
184+
plot_wavefield(u_full[-1])
185+
plot_shotrecord(recv)
186+
187+
# save numpy array
188+
# print("saving numpy array")
189+
# np.save("numerical_harmonic", u_full)
190+
191+
# load analytical array
192+
u_analytic = np.load("analytical_harmonic.npy")
193+
# plot comparison
194+
numerical_values = u_full[:, 256, 256]
195+
analytic_values = u_analytic[:, 256, 256]
196+
time_values = time_model.time_values
197+
plt.plot(time_values, analytic_values, time_values, numerical_values)
198+
plt.legend(["analytical", "numerical"])
199+
plt.title("Comparison between analytical and numerical result (simwave)")
200+
plt.xlabel("time")
201+
plt.show()
202+
203+

mms/comparison.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
3+
# create analytical solution, then numerical and compare both
4+
python harmonic_solution.py
5+
python acoustic_2D_mms.py

mms/harmonic_solution.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""Generates an harmonic solution to the wave equation
2+
3+
div(grad(u)) - 1/c^2 * d^2u/dt^2 = f
4+
5+
where u = sin(kx*x) * sin(kz*z) * cos(w*t)
6+
"""
7+
8+
9+
# from simwave import plot_shotrecord
10+
import numpy as np
11+
import matplotlib.pyplot as plt
12+
# fig = plt.figure()
13+
# ax = fig.add_subplot(projection='3d')
14+
# ax.set_zlim(-1.01, 1.01)
15+
16+
# Domain info
17+
dz, dx = (10, 10)
18+
zmin, zmax, xmin, xmax = (0, 5120, 0, 5120)
19+
nz = int((zmax - zmin) / dz + 1)
20+
nx = int((xmax - xmin) / dx + 1)
21+
zcoords = np.linspace(zmin, zmax, nz)
22+
xcoords = np.linspace(xmin, xmax, nx)
23+
Z, X = np.meshgrid(zcoords, xcoords)
24+
25+
# Params
26+
c = 1500
27+
freq= 5
28+
w = 2*np.pi*freq
29+
kz = np.pi / (zmax - zmin)
30+
kx = np.pi / (xmax - xmin)
31+
32+
# Time info
33+
t0 = 0
34+
tf = 1.5
35+
nt = 369
36+
tp = np.linspace(t0, tf, nt)
37+
38+
# Solution
39+
space_solution = np.sin(kx*X) * np.sin(kz*Z)
40+
time_solution = np.cos(w*tp)
41+
solution = (space_solution[:,:,None] * time_solution).T
42+
43+
# Save it
44+
print("saving analytical solution to .npy file")
45+
np.save("analytical_harmonic", solution)
46+

simwave/kernel/frontend/solver.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,11 @@ def forward(self, initial_grid=None):
132132

133133
initial_grid = self.space_model.dtype(initial_grid)
134134

135-
if initial_grid.shape != self.space_model.shape:
136-
raise ValueError(
137-
"initial_grid must have shape = {}").format(
138-
self.space_model.shape
139-
)
135+
# if initial_grid.shape != self.space_model.shape:
136+
# raise ValueError(
137+
# "initial_grid must have shape = {}").format(
138+
# self.space_model.shape
139+
# )
140140

141141
halo = self.space_model.halo_size[0]
142142
nbl = self.space_model.nbl
@@ -152,8 +152,8 @@ def forward(self, initial_grid=None):
152152
x1 = halo + nbl[2]
153153
x2 = halo + nbl[3]
154154

155-
u[0, z1:-z2, x1:-x2] = initial_grid
156-
u[1, z1:-z2, x1:-x2] = initial_grid
155+
u[0, z1:-z2, x1:-x2] = initial_grid[0]
156+
u[1, z1:-z2, x1:-x2] = initial_grid[1]
157157

158158
else:
159159

@@ -164,8 +164,8 @@ def forward(self, initial_grid=None):
164164
y1 = halo + nbl[4]
165165
y2 = halo + nbl[5]
166166

167-
u[0, z1:-z2, x1:-x2, y1:-y2] = initial_grid
168-
u[1, z1:-z2, x1:-x2, y1:-y2] = initial_grid
167+
u[0, z1:-z2, x1:-x2, y1:-y2] = initial_grid[0]
168+
u[1, z1:-z2, x1:-x2, y1:-y2] = initial_grid[1]
169169

170170
u_full, recv = self._middleware.exec(
171171
operator='forward',

0 commit comments

Comments
 (0)