|
| 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 | + |
0 commit comments