-
Notifications
You must be signed in to change notification settings - Fork 32
/
at_array_as_source.py
161 lines (126 loc) · 5.35 KB
/
at_array_as_source.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import colors
from matplotlib.animation import FuncAnimation
from kwave.data import Vector
from kwave.kgrid import kWaveGrid
from kwave.kmedium import kWaveMedium
from kwave.ksensor import kSensor
from kwave.ksource import kSource
from kwave.kspaceFirstOrder2D import kspace_first_order_2d_gpu
from kwave.options.simulation_execution_options import SimulationExecutionOptions
from kwave.options.simulation_options import SimulationOptions
from kwave.utils.kwave_array import kWaveArray
from kwave.utils.colormap import get_color_map
from kwave.utils.signals import tone_burst
def main():
# =========================================================================
# DEFINE KWAVEARRAY
# =========================================================================
# create empty array
karray = kWaveArray()
# define arc properties
radius = 50e-3 # [m]
diameter = 30e-3 # [m]
focus_pos = [-20e-3, 0] # [m]
# add arc-shaped element
elem_pos = [10e-3, -40e-3] # [m]
karray.add_arc_element(elem_pos, radius, diameter, focus_pos)
# add arc-shaped element
elem_pos = [20e-3, 0] # [m]
karray.add_arc_element(elem_pos, radius, diameter, focus_pos)
# add arc-shaped element
elem_pos = [10e-3, 40e-3] # [m]
karray.add_arc_element(elem_pos, radius, diameter, focus_pos)
# move the array down 10 mm, and rotate by 10 degrees (this moves all the
# elements together)
karray.set_array_position([10e-3, 0], 10)
# =========================================================================
# DEFINE GRID PROPERTIES
# =========================================================================
# grid properties
Nx = 256
dx = 0.5e-3
Ny = 256
dy = 0.5e-3
kgrid = kWaveGrid(Vector([Nx, Ny]), Vector([dx, dy]))
# medium properties
medium = kWaveMedium(sound_speed=1500)
# time array
kgrid.makeTime(medium.sound_speed)
# =========================================================================
# SIMULATION
# =========================================================================
# assign binary mask from karray to the source mask
source_p_mask = karray.get_array_binary_mask(kgrid)
# set source signals, one for each physical array element
f1 = 100e3
f2 = 200e3
f3 = 500e3
sig1 = tone_burst(1 / kgrid.dt, f1, 3).squeeze()
sig2 = tone_burst(1 / kgrid.dt, f2, 5).squeeze()
sig3 = tone_burst(1 / kgrid.dt, f3, 5).squeeze()
# combine source signals into one array
source_signal = np.zeros((3, max(len(sig1), len(sig2))))
source_signal[0, : len(sig1)] = sig1
source_signal[1, : len(sig2)] = sig2
source_signal[2, : len(sig3)] = sig3
# get distributed source signals (this automatically returns a weighted
# source signal for each grid point that forms part of the source)
source_p = karray.get_distributed_source_signal(kgrid, source_signal)
simulation_options = SimulationOptions(
save_to_disk=True,
data_cast="single",
)
execution_options = SimulationExecutionOptions(is_gpu_simulation=True)
# run k-Wave simulation (no sensor is used for this example)
# TODO: I would say proper behaviour would be to return the entire pressure field if sensor is None
sensor = kSensor()
sensor.mask = np.ones((Nx, Ny), dtype=bool)
source = kSource()
source.p_mask = source_p_mask
source.p = source_p
p = kspace_first_order_2d_gpu(kgrid, source, sensor, medium, simulation_options, execution_options)
p_field = np.reshape(p["p"], (kgrid.Nt, Nx, Ny))
p_field = np.transpose(p_field, (0, 2, 1))
# =========================================================================
# VISUALIZATION
# =========================================================================
# Normalize frames based on the maximum value over all frames
max_value = np.max(p_field)
normalized_frames = p_field / max_value
cmap = get_color_map()
# Create a figure and axis
fig, ax = plt.subplots()
# Create an empty image with the first normalized frame
image = ax.imshow(normalized_frames[0], cmap=cmap, norm=colors.Normalize(vmin=0, vmax=1))
# Function to update the image for each frame
def update(frame):
image.set_data(normalized_frames[frame])
ax.set_title(f"Frame {frame + 1}/{kgrid.Nt}")
return [image]
# Create the animation
ani = FuncAnimation(fig, update, frames=kgrid.Nt, interval=100) # Adjust interval as needed (in milliseconds)
# Save the animation as a video file (e.g., MP4)
video_filename = "output_video1.mp4"
ani.save("./" + video_filename, writer="ffmpeg", fps=30) # Adjust FPS as needed
# Show the animation (optional)
plt.show()
# Show the animation (optional)
plt.show()
# create pml mask (default size in 2D is 20 grid points)
pml_size = 20
pml_mask = np.zeros((Nx, Ny), dtype=bool)
pml_mask[:pml_size, :] = 1
pml_mask[:, :pml_size] = 1
pml_mask[-pml_size:, :] = 1
pml_mask[:, -pml_size:] = 1
# plot source and pml masks
plt.figure()
plt.imshow(np.logical_not(np.squeeze(source.p_mask | pml_mask)), aspect="auto", cmap="gray")
plt.xlabel("x-position [m]")
plt.ylabel("y-position [m]")
plt.title("Source and PML Masks")
plt.show()
if __name__ == "__main__":
main()