1use num_complex::Complex64;
13use serde::{Deserialize, Serialize};
14
15pub mod solutions_1d;
16pub mod solutions_2d;
17pub mod solutions_3d;
18
19pub use solutions_1d::*;
20pub use solutions_2d::*;
21pub use solutions_3d::*;
22
23#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
25pub struct Point {
26 pub x: f64,
28 pub y: f64,
30 pub z: f64,
32}
33
34impl Point {
35 pub fn new_1d(x: f64) -> Self {
37 Self { x, y: 0.0, z: 0.0 }
38 }
39
40 pub fn new_2d(x: f64, y: f64) -> Self {
42 Self { x, y, z: 0.0 }
43 }
44
45 pub fn new_3d(x: f64, y: f64, z: f64) -> Self {
47 Self { x, y, z }
48 }
49
50 pub fn from_polar(r: f64, theta: f64) -> Self {
52 Self::new_2d(r * theta.cos(), r * theta.sin())
53 }
54
55 pub fn from_spherical(r: f64, theta: f64, phi: f64) -> Self {
57 Self::new_3d(
58 r * theta.sin() * phi.cos(),
59 r * theta.sin() * phi.sin(),
60 r * theta.cos(),
61 )
62 }
63
64 pub fn radius(&self) -> f64 {
66 (self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
67 }
68
69 pub fn theta_2d(&self) -> f64 {
71 self.y.atan2(self.x)
72 }
73
74 pub fn theta_3d(&self) -> f64 {
76 (self.z / self.radius()).acos()
77 }
78
79 pub fn phi_3d(&self) -> f64 {
81 self.y.atan2(self.x)
82 }
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct AnalyticalSolution {
88 pub name: String,
90 pub dimensions: usize,
92 pub positions: Vec<Point>,
94 pub pressure: Vec<Complex64>,
96 pub wave_number: f64,
98 pub frequency: f64,
100 pub metadata: serde_json::Value,
102}
103
104impl AnalyticalSolution {
105 pub fn magnitude(&self) -> Vec<f64> {
107 self.pressure.iter().map(|p| p.norm()).collect()
108 }
109
110 pub fn phase(&self) -> Vec<f64> {
112 self.pressure.iter().map(|p| p.arg()).collect()
113 }
114
115 pub fn real(&self) -> Vec<f64> {
117 self.pressure.iter().map(|p| p.re).collect()
118 }
119
120 pub fn imag(&self) -> Vec<f64> {
122 self.pressure.iter().map(|p| p.im).collect()
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_point_creation() {
132 let p1d = Point::new_1d(1.0);
133 assert_eq!(p1d.x, 1.0);
134 assert_eq!(p1d.y, 0.0);
135 assert_eq!(p1d.z, 0.0);
136
137 let p2d = Point::from_polar(1.0, std::f64::consts::PI / 4.0);
138 assert!((p2d.radius() - 1.0).abs() < 1e-10);
139
140 let p3d = Point::from_spherical(1.0, std::f64::consts::PI / 2.0, 0.0);
141 assert!((p3d.radius() - 1.0).abs() < 1e-10);
142 }
143}