1use crate::grid::Grid2D;
4
5#[derive(Debug, Clone, Copy, PartialEq)]
7pub enum Waveform {
8 Ricker { freq: f32 },
11 Sine { freq: f32 },
13}
14
15impl Waveform {
16 pub fn eval(&self, t: f64) -> f32 {
18 match *self {
19 Waveform::Ricker { freq } => {
20 let f = freq as f64;
21 let a = (std::f64::consts::PI * f * (t - 1.0 / f)).powi(2);
22 ((1.0 - 2.0 * a) * (-a).exp()) as f32
23 }
24 Waveform::Sine { freq } => (2.0 * std::f64::consts::PI * freq as f64 * t).sin() as f32,
25 }
26 }
27}
28
29#[derive(Debug, Clone)]
30struct Source {
31 x: usize,
32 y: usize,
33 waveform: Waveform,
34 amplitude: f32,
35}
36
37pub struct AcousticSolver2D {
44 pub p: Grid2D,
45 pub vx: Grid2D,
46 pub vy: Grid2D,
47 rho: Grid2D,
48 inv_rho: Grid2D,
49 bulk_modulus: Grid2D,
50 damp: Grid2D,
52 sigma_x: Grid2D,
54 sigma_y: Grid2D,
55
56 zero_row: Vec<f32>,
57 sources: Vec<Source>,
58 receivers: Vec<(usize, usize)>,
59 traces: Vec<Vec<f32>>,
60 time: f64,
61}
62
63impl AcousticSolver2D {
64 pub fn new(nx: usize, ny: usize, dx: f32, dy: f32, default_rho: f32, default_c: f32) -> Self {
65 assert!(nx >= 2 && ny >= 2, "la grille doit faire au moins 2x2");
66 assert!(
67 default_rho > 0.0 && default_c > 0.0,
68 "rho et c doivent être > 0"
69 );
70
71 let mut rho = Grid2D::new(nx, ny, dx, dy);
72 rho.fill(default_rho);
73 let mut inv_rho = Grid2D::new(nx, ny, dx, dy);
74 inv_rho.fill(1.0 / default_rho);
75 let mut bulk_modulus = Grid2D::new(nx, ny, dx, dy);
76 bulk_modulus.fill(default_rho * default_c * default_c);
77 let mut damp = Grid2D::new(nx, ny, dx, dy);
78 damp.fill(1.0);
79
80 let sigma_x = Grid2D::new(nx, ny, dx, dy);
82 let sigma_y = Grid2D::new(nx, ny, dx, dy);
83
84 Self {
85 p: Grid2D::new(nx, ny, dx, dy),
86 vx: Grid2D::new(nx, ny, dx, dy),
87 vy: Grid2D::new(nx, ny, dx, dy),
88 rho,
89 inv_rho,
90 bulk_modulus,
91 damp,
92 sigma_x, sigma_y, zero_row: vec![0.0; nx],
95 sources: Vec::new(),
96 receivers: Vec::new(),
97 traces: Vec::new(),
98 time: 0.0,
99 }
100 }
101
102 pub fn rho(&self) -> &Grid2D {
106 &self.rho
107 }
108
109 pub fn set_pml_boundary(&mut self, width: usize, max_sigma: f32) {
113 let (nx, ny) = (self.p.nx, self.p.ny);
114
115 for y in 0..ny {
116 for x in 0..nx {
117 let dist_x_left = if x < width { width - x } else { 0 };
119 let dist_x_right = if x >= nx - width {
120 x - (nx - 1 - width)
121 } else {
122 0
123 };
124 let dist_x = dist_x_left.max(dist_x_right) as f32;
125
126 if width > 0 && dist_x > 0.0 {
127 let t = dist_x / width as f32;
128 let val = max_sigma * t * t; self.sigma_x.set(x, y, val);
130 } else {
131 self.sigma_x.set(x, y, 0.0);
132 }
133
134 let dist_y_top = if y < width { width - y } else { 0 };
136 let dist_y_bottom = if y >= ny - width {
137 y - (ny - 1 - width)
138 } else {
139 0
140 };
141 let dist_y = dist_y_top.max(dist_y_bottom) as f32;
142
143 if width > 0 && dist_y > 0.0 {
144 let t = dist_y / width as f32;
145 let val = max_sigma * t * t;
146 self.sigma_y.set(x, y, val);
147 } else {
148 self.sigma_y.set(x, y, 0.0);
149 }
150 }
151 }
152 }
153
154 pub fn bulk_modulus(&self) -> &Grid2D {
156 &self.bulk_modulus
157 }
158
159 pub fn set_medium(&mut self, x: usize, y: usize, rho: f32, c: f32) {
161 assert!(rho > 0.0 && c > 0.0, "rho et c doivent être > 0");
162 self.rho.set(x, y, rho);
163 self.inv_rho.set(x, y, 1.0 / rho);
164 self.bulk_modulus.set(x, y, rho * c * c);
165 }
166
167 pub fn set_medium_rect(
169 &mut self,
170 x0: usize,
171 y0: usize,
172 x1: usize,
173 y1: usize,
174 rho: f32,
175 c: f32,
176 ) {
177 for y in y0..y1.min(self.p.ny) {
178 for x in x0..x1.min(self.p.nx) {
179 self.set_medium(x, y, rho, c);
180 }
181 }
182 }
183
184 pub fn c_max(&self) -> f32 {
186 self.bulk_modulus
187 .as_slice()
188 .iter()
189 .zip(self.inv_rho.as_slice())
190 .map(|(k, ir)| (k * ir).sqrt())
191 .fold(0.0, f32::max)
192 }
193
194 pub fn max_stable_dt(&self) -> f32 {
196 let inv = (1.0 / (self.p.dx * self.p.dx) + 1.0 / (self.p.dy * self.p.dy)).sqrt();
197 1.0 / (self.c_max() * inv)
198 }
199
200 pub fn stable_dt(&self, safety: f32) -> f32 {
202 safety * self.max_stable_dt()
203 }
204
205 pub fn set_absorbing_boundary(&mut self, width: usize, max_damping: f32) {
214 assert!(
215 (0.0..1.0).contains(&max_damping),
216 "max_damping doit être dans [0, 1)"
217 );
218 let (nx, ny) = (self.p.nx, self.p.ny);
219 for y in 0..ny {
220 for x in 0..nx {
221 let d = x.min(nx - 1 - x).min(y).min(ny - 1 - y);
222 let f = if width > 0 && d < width {
223 let t = (width - d) as f32 / width as f32;
224 1.0 - max_damping * t * t
225 } else {
226 1.0
227 };
228 self.damp.set(x, y, f);
229 }
230 }
231 }
232
233 pub fn add_source(&mut self, x: usize, y: usize, waveform: Waveform, amplitude: f32) {
237 assert!(x < self.p.nx && y < self.p.ny, "source hors grille");
238 self.sources.push(Source {
239 x,
240 y,
241 waveform,
242 amplitude,
243 });
244 }
245
246 pub fn inject_source(&mut self, x: usize, y: usize, value: f32) {
248 let current = self.p.get(x, y);
249 self.p.set(x, y, current + value);
250 }
251
252 pub fn add_receiver(&mut self, x: usize, y: usize) -> usize {
254 assert!(x < self.p.nx && y < self.p.ny, "récepteur hors grille");
255 self.receivers.push((x, y));
256 self.traces.push(Vec::new());
257 self.receivers.len() - 1
258 }
259
260 pub fn receiver(&self, id: usize) -> &[f32] {
262 &self.traces[id]
263 }
264
265 pub fn time(&self) -> f64 {
267 self.time
268 }
269
270 pub fn step(&mut self, dt: f32) {
274 let nx = self.p.nx;
275 let ny = self.p.ny;
276 let inv_dx = 1.0 / self.p.dx;
277 let inv_dy = 1.0 / self.p.dy;
278
279 for s in &self.sources {
281 let idx = self.p.index(s.x, s.y);
282 self.p.as_mut_slice()[idx] += s.amplitude * s.waveform.eval(self.time);
283 }
284
285 {
287 let p = self.p.as_slice();
288 let inv_rho = self.inv_rho.as_slice();
289 let sig_x = self.sigma_x.as_slice();
290 let sig_y = self.sigma_y.as_slice();
291
292 let vel = |y: usize, vxr: &mut [f32], vyr: &mut [f32]| {
293 let o = y * nx;
294 let prow = &p[o..o + nx];
295 let irow = &inv_rho[o..o + nx];
296 let sx_row = &sig_x[o..o + nx];
297 let vxr = &mut vxr[..nx];
298
299 for i in 0..nx - 1 {
300 let damping_x = 1.0 / (1.0 + 0.5 * dt * sx_row[i]);
301 let dp_dx = (prow[i + 1] - prow[i]) * inv_dx;
302 vxr[i] = (vxr[i] - dt * irow[i] * dp_dx) * damping_x;
303 }
304
305 if y + 1 < ny {
306 let pn = &p[o + nx..o + 2 * nx];
307 let sy_next = &sig_y[o + nx..o + 2 * nx];
308 let vyr = &mut vyr[..nx];
309
310 for i in 0..nx {
311 let damping_y = 1.0 / (1.0 + 0.5 * dt * sy_next[i]);
312 let dp_dy = (pn[i] - prow[i]) * inv_dy;
313 vyr[i] = (vyr[i] - dt * irow[i] * dp_dy) * damping_y;
314 }
315 }
316 };
317 for_each_row2(self.vx.as_mut_slice(), self.vy.as_mut_slice(), nx, vel);
318 }
319
320 {
322 let vx = self.vx.as_slice();
323 let vy = self.vy.as_slice();
324 let bulk = self.bulk_modulus.as_slice();
325 let sig_x = self.sigma_x.as_slice();
326 let sig_y = self.sigma_y.as_slice();
327 let zeros = &self.zero_row[..];
328
329 let prs = |y: usize, prow: &mut [f32]| {
330 let o = y * nx;
331 let vxr = &vx[o..o + nx];
332 let vyr = &vy[o..o + nx];
333 let vyp: &[f32] = if y > 0 { &vy[o - nx..o] } else { zeros };
334 let kr = &bulk[o..o + nx];
335 let sx_row = &sig_x[o..o + nx];
336 let sy_row = &sig_y[o..o + nx];
337 let prow = &mut prow[..nx];
338
339 let div0 = vxr[0] * inv_dx + (vyr[0] - vyp[0]) * inv_dy;
341 let damping_0 = 1.0 / (1.0 + 0.5 * dt * (sx_row[0] + sy_row[0]));
342 prow[0] = (prow[0] - dt * kr[0] * div0) * damping_0;
343
344 for i in 1..nx {
345 let div = (vxr[i] - vxr[i - 1]) * inv_dx + (vyr[i] - vyp[i]) * inv_dy;
346 let damping = 1.0 / (1.0 + 0.5 * dt * (sx_row[i] + sy_row[i]));
347 prow[i] = (prow[i] - dt * kr[i] * div) * damping;
348 }
349 };
350 for_each_row1(self.p.as_mut_slice(), nx, prs);
351 }
352
353 for (&(x, y), trace) in self.receivers.iter().zip(self.traces.iter_mut()) {
355 trace.push(self.p.get(x, y));
356 }
357 self.time += dt as f64;
358 }
359}
360
361#[cfg(feature = "rayon")]
364fn for_each_row1<F>(a: &mut [f32], nx: usize, f: F)
365where
366 F: Fn(usize, &mut [f32]) + Sync + Send,
367{
368 use rayon::prelude::*;
369 a.par_chunks_mut(nx).enumerate().for_each(|(y, r)| f(y, r));
370}
371
372#[cfg(not(feature = "rayon"))]
373fn for_each_row1<F>(a: &mut [f32], nx: usize, f: F)
374where
375 F: Fn(usize, &mut [f32]) + Sync + Send,
376{
377 for (y, r) in a.chunks_mut(nx).enumerate() {
378 f(y, r);
379 }
380}
381
382#[cfg(feature = "rayon")]
383fn for_each_row2<F>(a: &mut [f32], b: &mut [f32], nx: usize, f: F)
384where
385 F: Fn(usize, &mut [f32], &mut [f32]) + Sync + Send,
386{
387 use rayon::prelude::*;
388 a.par_chunks_mut(nx)
389 .zip(b.par_chunks_mut(nx))
390 .enumerate()
391 .for_each(|(y, (ra, rb))| f(y, ra, rb));
392}
393
394#[cfg(not(feature = "rayon"))]
395fn for_each_row2<F>(a: &mut [f32], b: &mut [f32], nx: usize, f: F)
396where
397 F: Fn(usize, &mut [f32], &mut [f32]) + Sync + Send,
398{
399 for (y, (ra, rb)) in a.chunks_mut(nx).zip(b.chunks_mut(nx)).enumerate() {
400 f(y, ra, rb);
401 }
402}