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 } => {
25 (2.0 * std::f64::consts::PI * freq as f64 * t).sin() as f32
26 }
27 }
28 }
29}
30
31#[derive(Debug, Clone)]
32struct Source {
33 x: usize,
34 y: usize,
35 waveform: Waveform,
36 amplitude: f32,
37}
38
39pub struct AcousticSolver2D {
46 pub p: Grid2D,
47 pub vx: Grid2D,
48 pub vy: Grid2D,
49 rho: Grid2D,
50 inv_rho: Grid2D,
51 bulk_modulus: Grid2D,
52 damp: Grid2D,
54 zero_row: Vec<f32>,
55 sources: Vec<Source>,
56 receivers: Vec<(usize, usize)>,
57 traces: Vec<Vec<f32>>,
58 time: f64,
59}
60
61impl AcousticSolver2D {
62 pub fn new(nx: usize, ny: usize, dx: f32, dy: f32, default_rho: f32, default_c: f32) -> Self {
63 assert!(nx >= 2 && ny >= 2, "la grille doit faire au moins 2x2");
64 assert!(default_rho > 0.0 && default_c > 0.0, "rho et c doivent être > 0");
65
66 let mut rho = Grid2D::new(nx, ny, dx, dy);
67 rho.fill(default_rho);
68 let mut inv_rho = Grid2D::new(nx, ny, dx, dy);
69 inv_rho.fill(1.0 / default_rho);
70 let mut bulk_modulus = Grid2D::new(nx, ny, dx, dy);
71 bulk_modulus.fill(default_rho * default_c * default_c);
72 let mut damp = Grid2D::new(nx, ny, dx, dy);
73 damp.fill(1.0);
74
75 Self {
76 p: Grid2D::new(nx, ny, dx, dy),
77 vx: Grid2D::new(nx, ny, dx, dy),
78 vy: Grid2D::new(nx, ny, dx, dy),
79 rho,
80 inv_rho,
81 bulk_modulus,
82 damp,
83 zero_row: vec![0.0; nx],
84 sources: Vec::new(),
85 receivers: Vec::new(),
86 traces: Vec::new(),
87 time: 0.0,
88 }
89 }
90
91 pub fn rho(&self) -> &Grid2D {
95 &self.rho
96 }
97
98 pub fn bulk_modulus(&self) -> &Grid2D {
100 &self.bulk_modulus
101 }
102
103 pub fn set_medium(&mut self, x: usize, y: usize, rho: f32, c: f32) {
105 assert!(rho > 0.0 && c > 0.0, "rho et c doivent être > 0");
106 self.rho.set(x, y, rho);
107 self.inv_rho.set(x, y, 1.0 / rho);
108 self.bulk_modulus.set(x, y, rho * c * c);
109 }
110
111 pub fn set_medium_rect(&mut self, x0: usize, y0: usize, x1: usize, y1: usize, rho: f32, c: f32) {
113 for y in y0..y1.min(self.p.ny) {
114 for x in x0..x1.min(self.p.nx) {
115 self.set_medium(x, y, rho, c);
116 }
117 }
118 }
119
120 pub fn c_max(&self) -> f32 {
122 self.bulk_modulus
123 .as_slice()
124 .iter()
125 .zip(self.inv_rho.as_slice())
126 .map(|(k, ir)| (k * ir).sqrt())
127 .fold(0.0, f32::max)
128 }
129
130 pub fn max_stable_dt(&self) -> f32 {
132 let inv = (1.0 / (self.p.dx * self.p.dx) + 1.0 / (self.p.dy * self.p.dy)).sqrt();
133 1.0 / (self.c_max() * inv)
134 }
135
136 pub fn stable_dt(&self, safety: f32) -> f32 {
138 safety * self.max_stable_dt()
139 }
140
141 pub fn set_absorbing_boundary(&mut self, width: usize, max_damping: f32) {
150 assert!((0.0..1.0).contains(&max_damping), "max_damping doit être dans [0, 1)");
151 let (nx, ny) = (self.p.nx, self.p.ny);
152 for y in 0..ny {
153 for x in 0..nx {
154 let d = x.min(nx - 1 - x).min(y).min(ny - 1 - y);
155 let f = if width > 0 && d < width {
156 let t = (width - d) as f32 / width as f32;
157 1.0 - max_damping * t * t
158 } else {
159 1.0
160 };
161 self.damp.set(x, y, f);
162 }
163 }
164 }
165
166 pub fn add_source(&mut self, x: usize, y: usize, waveform: Waveform, amplitude: f32) {
170 assert!(x < self.p.nx && y < self.p.ny, "source hors grille");
171 self.sources.push(Source { x, y, waveform, amplitude });
172 }
173
174 pub fn inject_source(&mut self, x: usize, y: usize, value: f32) {
176 let current = self.p.get(x, y);
177 self.p.set(x, y, current + value);
178 }
179
180 pub fn add_receiver(&mut self, x: usize, y: usize) -> usize {
182 assert!(x < self.p.nx && y < self.p.ny, "récepteur hors grille");
183 self.receivers.push((x, y));
184 self.traces.push(Vec::new());
185 self.receivers.len() - 1
186 }
187
188 pub fn receiver(&self, id: usize) -> &[f32] {
190 &self.traces[id]
191 }
192
193 pub fn time(&self) -> f64 {
195 self.time
196 }
197
198 pub fn step(&mut self, dt: f32) {
202 let nx = self.p.nx;
203 let ny = self.p.ny;
204 let cx = dt / self.p.dx;
205 let cy = dt / self.p.dy;
206 let inv_dx = 1.0 / self.p.dx;
207 let inv_dy = 1.0 / self.p.dy;
208
209 for s in &self.sources {
211 let idx = self.p.index(s.x, s.y);
212 self.p.as_mut_slice()[idx] += s.amplitude * s.waveform.eval(self.time);
213 }
214
215 {
217 let p = self.p.as_slice();
218 let inv_rho = self.inv_rho.as_slice();
219 let damp = self.damp.as_slice();
220
221 let vel = |y: usize, vxr: &mut [f32], vyr: &mut [f32]| {
222 let o = y * nx;
223 let prow = &p[o..o + nx];
224 let irow = &inv_rho[o..o + nx];
225 let drow = &damp[o..o + nx];
226 let vxr = &mut vxr[..nx];
227 for i in 0..nx - 1 {
228 vxr[i] = (vxr[i] - cx * irow[i] * (prow[i + 1] - prow[i])) * drow[i];
229 }
230 if y + 1 < ny {
231 let pn = &p[o + nx..o + 2 * nx];
232 let vyr = &mut vyr[..nx];
233 for i in 0..nx {
234 vyr[i] = (vyr[i] - cy * irow[i] * (pn[i] - prow[i])) * drow[i];
235 }
236 }
237 };
238 for_each_row2(self.vx.as_mut_slice(), self.vy.as_mut_slice(), nx, vel);
239 }
240
241 {
243 let vx = self.vx.as_slice();
244 let vy = self.vy.as_slice();
245 let bulk = self.bulk_modulus.as_slice();
246 let damp = self.damp.as_slice();
247 let zeros = &self.zero_row[..];
248
249 let prs = |y: usize, prow: &mut [f32]| {
250 let o = y * nx;
251 let vxr = &vx[o..o + nx];
252 let vyr = &vy[o..o + nx];
253 let vyp: &[f32] = if y > 0 { &vy[o - nx..o] } else { zeros };
254 let kr = &bulk[o..o + nx];
255 let drow = &damp[o..o + nx];
256 let prow = &mut prow[..nx];
257
258 let div0 = vxr[0] * inv_dx + (vyr[0] - vyp[0]) * inv_dy;
260 prow[0] = (prow[0] - dt * kr[0] * div0) * drow[0];
261 for i in 1..nx {
262 let div = (vxr[i] - vxr[i - 1]) * inv_dx + (vyr[i] - vyp[i]) * inv_dy;
263 prow[i] = (prow[i] - dt * kr[i] * div) * drow[i];
264 }
265 };
266 for_each_row1(self.p.as_mut_slice(), nx, prs);
267 }
268
269 for (&(x, y), trace) in self.receivers.iter().zip(self.traces.iter_mut()) {
271 trace.push(self.p.get(x, y));
272 }
273 self.time += dt as f64;
274 }
275}
276
277#[cfg(feature = "rayon")]
280fn for_each_row1<F>(a: &mut [f32], nx: usize, f: F)
281where
282 F: Fn(usize, &mut [f32]) + Sync + Send,
283{
284 use rayon::prelude::*;
285 a.par_chunks_mut(nx).enumerate().for_each(|(y, r)| f(y, r));
286}
287
288#[cfg(not(feature = "rayon"))]
289fn for_each_row1<F>(a: &mut [f32], nx: usize, f: F)
290where
291 F: Fn(usize, &mut [f32]) + Sync + Send,
292{
293 for (y, r) in a.chunks_mut(nx).enumerate() {
294 f(y, r);
295 }
296}
297
298#[cfg(feature = "rayon")]
299fn for_each_row2<F>(a: &mut [f32], b: &mut [f32], nx: usize, f: F)
300where
301 F: Fn(usize, &mut [f32], &mut [f32]) + Sync + Send,
302{
303 use rayon::prelude::*;
304 a.par_chunks_mut(nx)
305 .zip(b.par_chunks_mut(nx))
306 .enumerate()
307 .for_each(|(y, (ra, rb))| f(y, ra, rb));
308}
309
310#[cfg(not(feature = "rayon"))]
311fn for_each_row2<F>(a: &mut [f32], b: &mut [f32], nx: usize, f: F)
312where
313 F: Fn(usize, &mut [f32], &mut [f32]) + Sync + Send,
314{
315 for (y, (ra, rb)) in a.chunks_mut(nx).zip(b.chunks_mut(nx)).enumerate() {
316 f(y, ra, rb);
317 }
318}