math_audio_wave/special/
spherical.rs1use num_complex::Complex64;
24
25pub fn spherical_bessel_j(order: usize, x: f64) -> Vec<f64> {
50 assert!(order >= 1, "Order must be at least 1");
51
52 let mut result = vec![0.0; order];
53
54 if x.abs() < 1e-15 {
56 result[0] = 1.0;
57 return result;
58 }
59
60 if x.abs() < 1e-10 {
61 result[0] = 1.0 - x * x / 6.0;
63 if order > 1 {
64 result[1] = x / 3.0;
65 }
66 for item in result.iter_mut().take(order).skip(2) {
67 *item = 0.0;
68 }
69 return result;
70 }
71
72 let start_n = order + (x.abs() as usize) + 20;
75
76 let mut values = vec![0.0; start_n + 2];
77 values[start_n + 1] = 0.0;
78 values[start_n] = 1e-30; for k in (0..start_n).rev() {
82 values[k] = (2 * k + 3) as f64 / x * values[k + 1] - values[k + 2];
83 }
84
85 let true_j0 = x.sin() / x;
87 let scale = true_j0 / values[0];
88
89 for n in 0..order {
90 result[n] = values[n] * scale;
91 }
92
93 result
94}
95
96pub fn spherical_bessel_y(order: usize, x: f64) -> Vec<f64> {
113 assert!(order >= 1, "Order must be at least 1");
114
115 let mut result = vec![0.0; order];
116
117 if x.abs() < 1e-15 {
118 for item in result.iter_mut().take(order) {
120 *item = f64::NEG_INFINITY;
121 }
122 return result;
123 }
124
125 let cos_x = x.cos();
126 let sin_x = x.sin();
127
128 result[0] = -cos_x / x;
130
131 if order == 1 {
132 return result;
133 }
134
135 result[1] = -cos_x / (x * x) - sin_x / x;
137
138 for n in 2..order {
140 result[n] = (2 * n - 1) as f64 / x * result[n - 1] - result[n - 2];
141 }
142
143 result
144}
145
146pub fn spherical_hankel_first_kind(order: usize, x: f64, harmonic_factor: f64) -> Vec<Complex64> {
166 assert!(order >= 2, "Order must be at least 2");
167 assert!(x > 0.0, "Argument must be positive");
168
169 let mut result = vec![Complex64::new(0.0, 0.0); order];
170
171 let cos_x = x.cos();
173 let sin_x = x.sin();
174
175 let mut y_n = vec![0.0; order];
176 y_n[0] = -cos_x / x;
177 y_n[1] = -(cos_x / x + sin_x) / x;
178
179 for n in 2..order {
180 y_n[n] = (2 * n - 1) as f64 / x * y_n[n - 1] - y_n[n - 2];
181 }
182
183 let nu = (order - 1) as f64;
186 let eps_gn = 1e-9;
187
188 let mut di = (2.0 * (nu + 1.0) + 1.0) / x;
190 let mut cj = di;
191 let mut dj = 0.0;
192 let mut err_gn = 1.0;
193 let mut j = 1;
194
195 while err_gn > eps_gn {
196 let aj = -1.0;
197 let bj = (2.0 * (nu + j as f64 + 1.0) + 1.0) / x;
198
199 dj = bj + aj * dj;
200 if dj == 0.0 {
201 dj = 1e-30;
202 }
203 dj = 1.0 / dj;
204
205 cj = bj + aj / cj;
206 if cj == 0.0 {
207 cj = 1e-30;
208 }
209
210 di = di * cj * dj;
211 err_gn = (cj * dj - 1.0).abs();
212 j += 1;
213
214 if j > 1000 {
215 break;
217 }
218 }
219
220 let gnu = nu / x - 1.0 / di;
221
222 let mut gg_n = vec![0.0; order];
224 let mut dg_n = vec![0.0; order];
225
226 gg_n[order - 1] = 1.0;
227 dg_n[order - 1] = gnu;
228
229 for i in (0..order - 1).rev() {
230 let di = i as f64;
231 gg_n[i] = (di + 2.0) / x * gg_n[i + 1] + dg_n[i + 1];
232 dg_n[i] = di / x * gg_n[i] - gg_n[i + 1];
233 }
234
235 let dp = if gg_n[0].abs() > 1e-5 {
237 sin_x / x / gg_n[0]
238 } else {
239 (cos_x - sin_x / x) / x / dg_n[0]
240 };
241
242 for n in 0..order {
244 result[n] = Complex64::new(dp * gg_n[n], harmonic_factor * y_n[n]);
245 }
246
247 result
248}
249
250pub fn spherical_bessel_j_derivative(order: usize, x: f64) -> Vec<f64> {
257 let j = spherical_bessel_j(order + 1, x);
258 let mut result = vec![0.0; order];
259
260 for n in 0..order {
261 if n == 0 {
262 result[0] = -j[1];
264 } else {
265 result[n] = j[n - 1] - (n + 1) as f64 / x * j[n];
266 }
267 }
268
269 result
270}
271
272pub fn spherical_bessel_y_derivative(order: usize, x: f64) -> Vec<f64> {
279 let y = spherical_bessel_y(order + 1, x);
280 let mut result = vec![0.0; order];
281
282 for n in 0..order {
283 if n == 0 {
284 result[0] = -y[1];
286 } else {
287 result[n] = y[n - 1] - (n + 1) as f64 / x * y[n];
288 }
289 }
290
291 result
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297 use std::f64::consts::PI;
298
299 const EPSILON: f64 = 1e-10;
300
301 #[test]
302 fn test_spherical_bessel_j0() {
303 let j = spherical_bessel_j(1, 1.0);
305 let expected = 1.0_f64.sin() / 1.0;
306 assert!((j[0] - expected).abs() < EPSILON);
307
308 let j = spherical_bessel_j(1, PI);
309 let expected = PI.sin() / PI;
310 assert!((j[0] - expected).abs() < EPSILON);
311 }
312
313 #[test]
314 fn test_spherical_bessel_j1() {
315 let x = 2.0;
317 let j = spherical_bessel_j(2, x);
318 let expected = x.sin() / (x * x) - x.cos() / x;
319 assert!((j[1] - expected).abs() < EPSILON);
320 }
321
322 #[test]
323 fn test_spherical_bessel_y0() {
324 let x = 1.0;
326 let y = spherical_bessel_y(1, x);
327 let expected = -x.cos() / x;
328 assert!((y[0] - expected).abs() < EPSILON);
329 }
330
331 #[test]
332 fn test_spherical_bessel_y1() {
333 let x = 2.0;
335 let y = spherical_bessel_y(2, x);
336 let expected = -x.cos() / (x * x) - x.sin() / x;
337 assert!((y[1] - expected).abs() < EPSILON);
338 }
339
340 #[test]
341 fn test_spherical_hankel_consistency() {
342 let x = 3.0;
344 let order = 5;
345 let j = spherical_bessel_j(order, x);
346 let y = spherical_bessel_y(order, x);
347 let h = spherical_hankel_first_kind(order, x, 1.0);
348
349 for n in 0..order {
350 assert!(
351 (h[n].re - j[n]).abs() < 1e-8,
352 "Real part mismatch at n={}: {} vs {}",
353 n,
354 h[n].re,
355 j[n]
356 );
357 assert!(
358 (h[n].im - y[n]).abs() < 1e-8,
359 "Imag part mismatch at n={}: {} vs {}",
360 n,
361 h[n].im,
362 y[n]
363 );
364 }
365 }
366
367 #[test]
368 fn test_hankel_asymptotic() {
369 let x = 50.0;
371 let h = spherical_hankel_first_kind(3, x, 1.0);
372
373 let expected_re = x.sin() / x;
375 let expected_im = -x.cos() / x;
376
377 assert!(
378 (h[0].re - expected_re).abs() < 0.01,
379 "Asymptotic real mismatch"
380 );
381 assert!(
382 (h[0].im - expected_im).abs() < 0.01,
383 "Asymptotic imag mismatch"
384 );
385 }
386
387 #[test]
388 fn test_bessel_derivative_j0() {
389 let x = 2.0;
391 let jp = spherical_bessel_j_derivative(1, x);
392 let j = spherical_bessel_j(2, x);
393 assert!((jp[0] + j[1]).abs() < EPSILON);
394 }
395
396 #[test]
397 fn test_recurrence_stability() {
398 let x = 5.0;
400 let order = 20;
401 let j = spherical_bessel_j(order, x);
402
403 for n in 0..order {
405 assert!(j[n].is_finite(), "j_{} is not finite", n);
406 }
407
408 assert!(j[15].abs() < j[5].abs());
410 }
411}